在上一篇博客
《JAVA并发-显式锁(一)》中介绍了Lock和ReadWriteLock的基本用法,下面来看看如何使用Lock的tryLock()方法来避免死锁。
在博客 《JAVA并发-3种典型的死锁》中有一个动态的锁顺序死锁。我们使用System.identifyHashCode()来固定线程获取锁的顺序,从而解决了死锁问题。学习了Lock之后,可以使用tryLock()来解决动态的锁顺序死锁问题。代码如下:
可以看到,我们 使用tryLock实现了轮循锁和定时锁。tryLock会尝试获得2个锁,如果不能同时获得,那就释放已获得的锁然后重试。如果在指定的时间内不能获得所有锁,那就返回一个失败的状态。
下面介绍下与锁相关的几个概念:
1. 可重入锁:什么叫可重入锁呢?如下例子:当一个线程持有锁并执行fun1方法时,他不再需要重新申请该对象锁来执行fun2方法(实际上,如果需要重信申请的话,就死锁了),此时我们说synchronized是可重入的。实际上,synchronized和ReentrantLock都是可重入的锁。
可重入锁说明,锁的分配机制是基于线程的,而不是基于方法的。
2. 可中断锁:可以响应中断的锁,当一个线程正在等待锁时,可以响应中断。synchronized是不可中断锁,而Lock是可中断锁。
在博客 《JAVA并发-3种典型的死锁》中有一个动态的锁顺序死锁。我们使用System.identifyHashCode()来固定线程获取锁的顺序,从而解决了死锁问题。学习了Lock之后,可以使用tryLock()来解决动态的锁顺序死锁问题。代码如下:
class Test {
public boolean transfer(Account from,Account to,int num,long timeout,TimeUnit unit){
long endTime=System.nanoTime()+unit.toNanos(timeout);
while(endTime>System.nanoTime()){
if(from.lock.tryLock()){
try{
if(to.lock.tryLock()){
try{
//转账操作
return true;
}finally{
to.lock.unlock();
}
}
}finally{
from.lock.unlock();
}
}
}
return false;
}
}
class Account{
public Lock lock=new ReentrantLock();
//...
}
可以看到,我们 使用tryLock实现了轮循锁和定时锁。tryLock会尝试获得2个锁,如果不能同时获得,那就释放已获得的锁然后重试。如果在指定的时间内不能获得所有锁,那就返回一个失败的状态。
下面介绍下与锁相关的几个概念:
1. 可重入锁:什么叫可重入锁呢?如下例子:当一个线程持有锁并执行fun1方法时,他不再需要重新申请该对象锁来执行fun2方法(实际上,如果需要重信申请的话,就死锁了),此时我们说synchronized是可重入的。实际上,synchronized和ReentrantLock都是可重入的锁。
可重入锁说明,锁的分配机制是基于线程的,而不是基于方法的。
class Test {
public synchronized void fun1(){
fun2();
}
public synchronized void fun2(){
//doSomething
}
}
2. 可中断锁:可以响应中断的锁,当一个线程正在等待锁时,可以响应中断。synchronized是不可中断锁,而Lock是可中断锁。
3. 公平锁:能够保证以请求锁的顺序来获得锁,就是公平锁。否则,就是非公平锁。我们希望做到公平,但实际应用中非公平锁的性能更好。在Java中,synchronized是非公平锁,它无法保证等待的线程获取锁的顺序。对于ReentrantLock和ReentrantReadWriteLock,它默认情况下是非公平锁,但是可以设置为公平锁。
笔者开设了一个知乎live,详细的介绍的JAVA从入门到精通该如何学,学什么?
提供给想深入学习和提高JAVA能力的同学,欢迎收听https://www.zhihu.com/lives/932192204248682496