技术文章第一时间送达!
这两天在加深多线程的相关知识,刚好遇到了ReentrantLock和ReentrantReadWriteLock这两个类,觉得后者的一些特性对多线程的性能还是提升很大的,挺有意思,特此分享一下,非常简单的哦。
进入正题,首先,先来介绍一下今天的主角之一:ReentrantLock。
我们都知道在Java多线程中,可以使用synchronized关键字来实现线程之间同
步互斥。
但在JDK1.5中新增加了ReentrantLock类也能达到同样的效果,并且在扩展功能上也更加强大,比如具有嗅探锁定、多路分支通知等功能,而且在使用上也比synchronized更加的灵活。
由来和作用,相信你已经知道了,纸上谈兵可能理解不深切,接下来亲身体验一下它的用法。(如了解可跳过这里,照顾一下基础薄弱的小伙伴)
ReentLock的简单用法:
创建Myservice类
创建一个线程A
创建一个线程B
启动类:
运行结果如下:
我们可以看到,线程A获取到锁的时间和线程B获取到锁的时间,是相差了5秒,在代码中,我们通过Thread.sleep(5000)使线程睡眠了5秒,证明,同一时刻只能有一个线程获取到锁,达到互斥的目的。
类ReentrantLock具有完全互斥排他的效果,即同一时间只有一个线程在执行ReentrantLock.lock()方法后面的任务。
但是呢?有没有发现一个问题。虽然ReentrantLock这样做虽然保证了实例变量的线程安全性,但效率确实非常低下的。
有没有好的方法呢?
这个时候,就是今天的另外一个主角登场的时候了:ReentrantReadWriteLock
针对上面问题在JDK中体用了另一种读写锁ReentrantReadWriteLock类,使用它可以加快运行效率,在某些不需要操作实例变量的方法中,完全可以使用读写锁ReentrantReadWriteLock来提升该方法的代码运行速度。
先来看看它的用法
创建一个Service类
创建一个线程A
/**
* 作者:LKP
* 时间:2018/11/27
*/
public class ThreadA extends Thread{
private Service service;
public ThreadA(Service service) {
this.service = service;
}
@Override
public void run() {
service.read();
}
}
创建一个线程B
/**
* 作者:LKP
* 时间:2018/11/27
*/
public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
this.service = service;
}
@Override
public void run() {
service.read();
}
}
启动类:
/**
* 作者:LKP
* 时间:2018/11/27
*/
public class Run {
public static void main(String[] args) {
Service service = new Service();
ThreadA a = new ThreadA(service);
ThreadB b = new ThreadB(service);
a.setName("A");
b.setName("B");
a.start();
b.start();
}
}
来看一下运行结果:
时间是一样的,为什么呢?难道是锁没起作用,哈哈哈,这就是它的一个特性,读读共享。简单来说,就是如果多个线程,都是进行读取操作,那么他们是可以同时获取到锁的。
再继续看看,修改Service类
其他的类不作修改,然后运行
你发现了什么?时间不一致,证明,同一时间只有一个线程获取到锁了,它们是互斥的,这就是另外一个特性:写写互斥。简单来说就是,当有多个线程进行写操作的时候,那么同时只有一个线程能获取到锁。
再继续往下看看
修改Service类
修改一下线程A类
/**
* 作者:LKP
* 时间:2018/11/27
*/
public class ThreadA extends Thread{
private Service service;
public ThreadA(Service service) {
this.service = service;
}
@Override
public void run() {
service.read();
}
}
修改一下线程B类
/**
* 作者:LKP
* 时间:2018/11/27
*/
public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
this.service = service;
}
@Override
public void run() {
service.wirte();
}
}
启动类可以不用修改的啦
/**
* 作者:LKP
* 时间:2018/11/27
*/
public class Run {
public static void main(String[] args) {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
}
}
运行结果
时间是不一样的,证明它们是互斥运行的,你想到了什么呢?没错,就是读写互斥。就是,当多个线程进入锁的时候,如果一个线程获取的是读锁,一个线程获取的是写锁,那么它们也是互斥的。
那如果先获取写锁,后获取读锁呢?
来试试
修改启动类
/**
* 作者:LKP
* 时间:2018/11/27
*/
public class Run {
public static void main(String[] args) {
Service service = new Service();
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
}
}
再来看看运行结果
证明先获取写锁,在获取读锁,它们是一样的,都是互斥进行的。
总结
读写锁表示也有两个锁,一个是读操作下相关的锁,也称为共享锁;另一个是写操作相关的锁,也叫排他锁。
也就是多个读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥。
在没有线程Thread进行写入操作时,进行读取操作的多个Thread都可以获取读锁,而进行写入操作的Thread只有在获取写锁后才能进行写入操作。
即多个Thread可以同时进行读取操作,但是同一时刻只允许一个Thread进行写入操作。
今天的文章就到这里了,有什么错误之处,欢迎号内留言
如果文章觉得不错,可以号内送朵:
你的支持,是我最大的动力
感谢阅读!