ReentrantLock具有完全互斥排他的效果,也即同一时间只有一个线程在执行ReentrantLock.lock()方法后边的任务。这样做保证了实例变量的线程安全性,但是效率低下。
ReentrantReadWriteLock类是ReadWriteLock接口的实现类,这是一个读写锁。读写锁维护了两个锁,一个是读相关的,也称之为共享锁;另外一个是写相关的,也叫排他锁。多个读锁之间不排斥,读锁与写锁互斥,写锁与写锁互斥。具体而言,就是多个线程可以同时进行读取操作,但是同一时刻只允许一个线程进行写入操作。
ReentarntReadWriteLock特性
- 公平性:默认是公平锁,同时支持非公平锁,吞吐量上来看是非公平优于公平
- 重入:支持重进入。读线程在获取读锁后,能再次获取读锁。写线程获取了写锁后能再次获取写锁,也能同时获取读锁。
- 锁降级:遵循获取写锁、获取读锁再释放写锁的次序,写锁能降级成为读锁
ReentarntReadWriteLock的使用
读读共享示例代码:
class Service{ private ReentrantReadWriteLock lock=new ReentrantReadWriteLock(); public void read(){ try { lock.readLock().lock(); System.out.println("获得读锁 "+Thread.currentThread().getName()+" "+new Date(System.currentTimeMillis())); Thread.sleep(50000); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.readLock().unlock(); } }}class MyThread extends Thread{ private Service service; public MyThread(Service service){ this.service=service; } @Override public void run() { service.read(); }}public class Run { public static void main(String[] args) { Service service=new Service(); MyThread a=new MyThread(service); a.setName("A"); MyThread b=new MyThread(service); b.setName("B"); a.start(); b.start(); }}复制代码
运行结果:
获得读锁 B Thu Dec 13 16:09:29 CST 2018获得读锁 A Thu Dec 13 16:09:29 CST 2018复制代码
可以发现,二者是近乎同时进行读取的,也就是读读是共享的。也就是说允许了多个线程同时执行lock()方法后面的代码。
对于写存在的情况,修改代码如下:
class Service{ private ReentrantReadWriteLock lock=new ReentrantReadWriteLock(); public void read(){ try { lock.readLock().lock(); System.out.println("获得读锁 "+Thread.currentThread().getName()+" "+new Date(System.currentTimeMillis())); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.readLock().unlock(); } } public void write(){ try { lock.writeLock().lock(); System.out.println("获得写锁 "+Thread.currentThread().getName()+" "+new Date(System.currentTimeMillis())); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.writeLock().unlock(); } }}class MyReadThread extends Thread{ private Service service; public MyReadThread(Service service){ this.service=service; } @Override public void run() { service.read(); }}class MyWriteThread extends Thread{ private Service service; public MyWriteThread(Service service){ this.service=service; } @Override public void run() { service.write(); }}public class Run { public static void main(String[] args) { Service service=new Service(); MyReadThread read1=new MyReadThread(service); read1.setName("read1"); MyReadThread read2=new MyReadThread(service); read2.setName("read2"); MyWriteThread write1=new MyWriteThread(service); write1.setName("write1"); MyWriteThread write2=new MyWriteThread(service); write2.setName("write2"); read1.start(); write1.start(); write2.start(); read2.start(); }}复制代码
运行结果:
获得写锁 write1 Thu Dec 13 16:16:24 CST 2018获得读锁 read1 Thu Dec 13 16:16:29 CST 2018获得写锁 write2 Thu Dec 13 16:16:34 CST 2018获得读锁 read2 Thu Dec 13 16:16:39 CST 2018复制代码
可以看到,四个线程是互斥的执行的,每个线程都是等待了5秒才执行。也就是说,只要出现写操作的过程就是互斥的。
参考资料
- 高洪岩. Java多线程编程核心技术[M]. 机械工业出版社, 2015