synchronized代表一种声明式编程,程序员更多的是表达一种同步声明,由Java系统负责具体实现,程序员不知道其实现细节,显式锁代表一种命令式编程,程序员实现所有细节。
声明式编程的好处除了简单,还在于性能,在较新版本的JVM上,ReentrantLock和synchronized的性能(还是自旋锁,但是避免了进入内核态阻塞状态)是接近的,但Java编译器和虚拟机可以不断优化synchronized的实现,比如,自动分析synchronized的使用,对于没有锁竞争的场景,自动省略对锁获取/释放的调用。
能用synchronized就用synchronized,除非确定要ReentrantLock独有功能
显式锁接口Lock的定义为:
public interface Lock {
void lock();//会阻塞直到成功
void unlock();
//可以响应中断
void lockInterruptibly() throws InterruptedException;
//尝试获取锁
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
Condition newCondition();
}
比synchronized灵活, 主要实现类是ReentrantLock
lock()/unlock()一般使用
public class Counter {
private final Lock lock = new ReentrantLock();
private volatile int count;
public void incr() {
lock.lock();//锁
try {
count++;
} finally {
//finally 里面放开
lock.unlock();
}
}
public int getCount() {
return count;
}
}
tryLock避免死锁
这样lock
拿不到锁不罢休,可能死锁,
/**
*
* @param from 来账户
* @param to 去账户
* @param money 转账金额
* @throws NoEnoughMoneyException
*/
public static void transfer_deadlock(Account from, Account to, double money)
throws NoEnoughMoneyException {
//如果相反的反向也在转,都拿到了第一个锁...都是在等另外一个锁就完了 死锁
from.lock();
try {
to.lock();
try {
if (from.getMoney() >= money) {
from.reduce(money);
to.add(money);
} else {
throw new NoEnoughMoneyException();
}
} finally {
to.unlock();
}
} finally {
from.unlock();
}
}
换成tryLock
拿不到就算了,不会死锁
public static boolean tryTransfer(Account from, Account to, double money)
throws NoEnoughMoneyException {
if (from.tryLock()) {
try {
//拿不到就算了 放弃 把之前拿到的锁也放掉 不会死锁
if (to.tryLock()) {
try {
if (from.getMoney() >= money) {
from.reduce(money);
to.add(money);
} else {
throw new NoEnoughMoneyException();
}
return true;
} finally {
to.unlock();
}
}
} finally {
from.unlock();
}
}
return false;
}