当前位置:网站首页>高级并发编程系列九(Lock接口分析)

高级并发编程系列九(Lock接口分析)

2020-11-07 18:55:00 yhhitall

1.考考你

许多朋友在入门java并发编程的时候,如果说到控制线程安全,一定对synchronized关键字非常熟系。synchronized关键字表示同步加锁,就是我正在用,你得等我用完后再来的意思。

比如在 高级并发编程系列七(锁入门)这一篇,我们对add_i变量自增操作,同步加锁一样,让add_i变量在同一时刻,只会被一个线程操作,从而线程安全。

/**
* addI方法,实现add_i变量自曾操作
*/
public synchronized static void addI(){
        add_i ++;
}

这么说,你可能就会有疑问了:既然synchronized关键字可以实现加锁,保障线程安全了。那么java的设计者为什么还要在juc包中提供锁接口Lock,以及Lock接口的相关实现呢?这不是既生瑜、何生亮吗?不好,难道周瑜孔明的故事还要再演一遍!

其实不是的,虽然有了synchronized关键字,我们说再有Lock接口和实现并不多余,那么接下来我们就一起来说道说道。

之所以有了synchronized关键字,还需要提供Lock接口,主要基于这么几个因素:

  • synchronized同步加锁内部由jdk控制,是非公平锁。实际应用中,有时候我们需要公平锁,但是它做不到。一句话概括:不够灵活

  • synchronized同步加锁内部由jdk控制,不可中断。使用起来比较霸道,一旦获取锁的线程未执行完,外部不可操控,不可中断获取锁线程。一句话概括:不够灵活

  • synchronized同步加锁内部由jdk控制,不能获取到加锁状态,即一个线程A获取到锁以后,那么需要同一个锁的线程BCD只能被阻塞等待,使用起来不够灵活。比如说做不到线程A获取锁以后,线程B来尝试获取锁,如果获取不到锁,线程B可以去做别的事情,不需要傻傻的等在这里。一句话概括:不够灵活

可以看到,综上所述我们对synchronized关键字的印象是:不够灵活。这里需要注意,有的朋友在说起synchronized关键字,与Lock锁区别的时候,会从性能的角度去看,事实上从jdk1.6以后,jvm内部专门实现了锁优化,两者在性能上几乎没有差异了。主要的区别还是synchronized不够灵活。

那么我们再来看一下,针对synchronized不灵活的地方,Lock接口都提供了哪些解决方案:

  • synchronized锁是非公平锁。Lock接口常用的实现类ReentrantLock,在构造锁对象的时候,我们可以指定构造公平锁,还是非公平锁,实现按需构造

  • synchronized锁是不可中断锁。Lock接口提供了lockInterruptibly()方法,支持中断操作

  • synchronized锁获取不到加锁状态。Lock接口提供了tryLock()方法,用于返回获取锁状态。如果线程A已经获取到锁,那么线程B通过tryLock尝试获取锁,获取不到不需要被阻塞

  • 另外Lock接口的实现类,通过在读写锁ReentrantReadWriteLock类中的读锁ReadLock,实现了共享锁。所谓共享锁读锁,即是大家都是读操作,不用太见外,你可以读,我可以读,大家读才是真的读。

通过以上关于Lock接口的描述,你是不是越来越爱Lock锁接口了,我想是的。那么接下来,我们一起来看一下Lock接口的设计,以及基本用法。

2.案例

2.1.Lock接口源码

通过以上分析描述,我想我们都会很好奇,Lock接口到底是如何设计的,它需要给我们提供哪些能力呢?对吧,我们一起来尝试思考一下:

  • 需要一个加锁的操作,因此应该有加锁方法:lock

  • 任务执行完成后,需要一个释放锁的操作,因此应该有释放锁的方法:unlock

  • 根据我们上面与synchronized对比,Lock需要支持获取锁状态,如果尝试获取不到锁,不需要阻塞线程,因此应该有尝试获取锁方法:tryLock

  • 根据我们上面与synchronized对比,Lock需要支持中断操作,因此应该有支持中断操作的加锁方法:lockInterruptibly

  • 以上应该是我们可以直观想到的Lock基础能力,事实上Lock接口通过不同的实现类,提供了更强大的能力。我们后续通过案例慢慢来展示,暂时你先不需要太关心

接下来让我们看一下,Lock接口的源码截图,除了看图以外,我建议你打开jdk的源码,看一看Lock接口源码,里面有很详细的源码注释,这是一种挺好的学习方式。

 

2.2.Lock接口编程模板

在你的应用中,如果是通过synchronized关键字加锁的话,你一定还有印象:使用synchronized关键字,我们并不需要释放锁的操作,一切都有jvm来控制

但是如果你选择了更加灵活的Lock锁方式,记得一定要:配套释放锁操作,千万不要只管生、不管养那就麻烦了,那么等待你的应该就是deadLock(死锁)了

接下来我先给一个通用的Lock锁使用模板,你需要留意try{...}finally{...}语句块。

这里我们暂时不展开实际的使用案例,实际应用案例,我们将放在下一篇在跟你一起来学习。

/**
* addI方法,实现add_i变量自曾操作
* 通过Lock加锁
*/
public  static void addI(){
    // 加锁
    lock.lock();
    try{
        // 业务操作
        add_i ++;
    }finally{
        // 释放锁
        lock.unlock();
    }
        
}

 

版权声明
本文为[yhhitall]所创,转载请带上原文链接,感谢
https://my.oschina.net/u/4450329/blog/4707654