ITPub博客

首页 > > > ReentrantLock会!ReentrantReadWriteLock呢?不懂快戳进来看看

ReentrantLock会!ReentrantReadWriteLock呢?不懂快戳进来看看

作者:孤独键客 时间:2020-05-13 11:43:43 0 删除 编辑


点击上方“程序员的成长之路”,选择“置顶公众号”

技术文章第一时间送达!


这两天在加深多线程的相关知识,刚好遇到了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进行写入操作。




今天的文章就到这里了,有什么错误之处,欢迎号内留言

如果文章觉得不错,可以号内送朵: 

你的支持,是我最大的动力

感谢阅读!


END


扫一扫

关注我们




来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69902700/viewspace-3143724/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
一个不羁的码农,终身学习者。公众号「程序员的成长之路」。目前就职于一家游戏公司,主要负责服务器端,以及web系统的开发工作,对Java相关知识比较擅长。

注册时间:2018-12-14

  • 博文量
    793
  • 访问量
    119764