ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[TOC] # ReadWriteLock ReadWriteLock也是一个接口,在它里面只定义了两个方法: ~~~ public interface ReadWriteLock { /** * Returns the lock used for reading. * * @return the lock used for reading. */ Lock readLock(); /** * Returns the lock used for writing. * * @return the lock used for writing. */ Lock writeLock(); } ~~~ 一个用来获取读锁,一个用来获取写锁。也就是说将文件的读写操作分开,分成2个锁来分配给线程,从而使得多个线程可以同时进行读操作。下面的ReentrantReadWriteLock实现了ReadWriteLock接口。 # ReentrantReadWriteLock ReentrantReadWriteLock里面提供了很多丰富的方法,不过最主要的有两个方法:readLock()和writeLock()用来获取读锁和写锁。 下面通过几个例子来看一下ReentrantReadWriteLock具体用法。 ## synchronized实现 我们先看一下,多线程同时读取文件时,用synchronized实现的效果 ~~~ package testThread; import java.util.concurrent.locks.ReentrantReadWriteLock; public class TestThread { private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); public static void main(String[] args) { final TestThread testThread = new TestThread(); new Thread() { public void run() { testThread.get(Thread.currentThread()); } }.start(); new Thread() { public void run() { testThread.get(Thread.currentThread()); } }.start(); } private synchronized void get(Thread thread) { long start = System.currentTimeMillis(); while (System.currentTimeMillis() - start <= 1) { System.out.println(thread.getName() + "正在进行读操作"); } System.out.println(thread.getName() + "读操作完毕"); } } ~~~ 这些线程是一次执行的 ## 读写锁读操作 用读写锁的话: ~~~ package com.study; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadAndWriteLock { /** * 获取读写锁 */ ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); public void get(Thread thread) { //用读锁锁定 lock.readLock().lock(); try { System.out.println("start time: " + System.currentTimeMillis()); for (int i = 0; i < 5; i++) { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(thread.getName() + " 正在进行读操作---"); } System.out.println(thread.getName() + " 读操作完毕---"); System.out.println("end time: " + System.currentTimeMillis()); } finally { lock.readLock().unlock(); } } public static void main(String[] args) { ReadAndWriteLock lock = new ReadAndWriteLock(); new Thread(() -> lock.get(Thread.currentThread())).start(); new Thread(() -> lock.get(Thread.currentThread())).start(); } } ~~~ 说明thread1和thread2在同时进行读操作。 这样就大大提升了读操作的效率。 这样就很巧妙的解决synchronized的一个性能问题:读与读之间互斥 ## 读写锁的互斥 ~~~ package com.study; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadAndWriteLock { /** * 获取读写锁 */ ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); /** * 读操作 */ public void readFile(Thread thread) { //获取读锁 lock.readLock().lock(); //判断是不是写锁 boolean readLock = lock.isWriteLocked(); if (!readLock) { System.out.println("当前为读锁!"); } try { for (int i = 0; i < 5; i++) { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(thread.getName() + "正在进行读操作"); } System.out.println(thread.getName() + "读操作完毕"); } finally { System.out.println("释放读锁"); lock.readLock().unlock(); } } /** * 写操作 */ public void writeFile(Thread thread) { //获取写锁 lock.writeLock().lock(); //判断是不是写锁 boolean readLock = lock.isWriteLocked(); if (readLock) { System.out.println("当前为写锁!"); } try { for (int i = 0; i < 5; i++) { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(thread.getName() + "正在进行写操作"); } System.out.println(thread.getName() + "写操作完毕"); } finally { System.out.println("释放写锁"); lock.writeLock().unlock(); } } public static void main(String[] args) { ReadAndWriteLock lock = new ReadAndWriteLock(); //建立n个线程同时读 ExecutorService service = Executors.newCachedThreadPool(); service.execute(new Runnable() { @Override public void run() { //测试2个写锁 //lock.writeFile(Thread.currentThread()); lock.readFile(Thread.currentThread()); } }); //建立n个线程同时写 ExecutorService service1 = Executors.newCachedThreadPool(); service1.execute(new Runnable() { @Override public void run() { //测试2个读锁 //lock.readFile(Thread.currentThread()); lock.writeFile(Thread.currentThread()); } }); } } ~~~ # 注意 不过要注意的是,如果有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁。 如果有一个线程已经占用了写锁,则此时其他线程如果申请写锁或者读锁,则申请的线程会一直等待释放写锁。 锁降级:写锁--没释放--可以降级到读锁 锁升级:读锁-没释放--可以升级到写锁 在分布式场景下,像读写锁这样的锁,加入不用锁降级的方式,可能会出现脏读的现象