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();
}
}
来源:oschina
链接:https://my.oschina.net/u/4450329/blog/4707654