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
- blog.csdn.net/qq_34337272…