Synchronized
Synchronized也称内置锁,Java提供同步代码块这种内置的锁机制来支持原子性,同步代码块包括两个部分:一个作为锁的对象引用,一个作为由这个锁保护的代码块。注意synchronized并不是“为这一块代码”加锁,而是给Class对象加锁。
synchronized (lock) {}
每一个Java对象都能用作实现同步的锁,线程在进入同步代码块之前会自动获得锁,在退出同步代码块时自动释放锁。锁是互斥的,最多只有一个线程能持有这个锁,当A线程持有一个锁,B线程尝试获取这个锁时,B线程只能等待或者阻塞,直到A线程释放这个锁。
内置锁是可重入的,一个线程获取一个由自己持有的锁,这样的请求是成功的。
ReentrantLock
ReentrantLock实现了Lock接口,Lock提供了一种无条件、可轮询、定时、可中断的锁获取操作,加锁和解锁都是显式的。ReentrantLock提供了跟跟内置锁相同的互斥性和内存可见性,提供了相同的可重入的特性。
使用ReentrantLock必须在finally中释放锁,否则如果在代码中抛异常,那这个锁就永远都不能释放。
// 示例
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
// ... method body
} finally {
lock.unlock();
}
Synchronized和ReentrantLock的区别
在大多数情况下,内置锁都能很好的完成工作,但是它存在一些局限性,比如,无法中断一个正在等待获取锁的线程。但ReentrantLock也不能完全代替Synchronized,ReentrantLock需要更多的try-catch-finally代码。
在你需要如下特性时,你可以选择使用ReentrantLock来解决你的问题:
- 轮询锁和定时锁
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
马上返回结果和带时间限制的tryLock方法。
- 可中断的锁获取操作
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
调用后一直阻塞到获得锁,但是接受中断信号。
- 非块结构的加锁
可重入的实现原理
重入的一种实现方法是,为每个锁关联一个获取计数值和一个所有者线程,当计数值为0时,这个锁就被认为是没有被任何线程持有。当线程请求一个未被持有的锁时,JVM将记下锁的持有者,并且将获取计数值置为1。如果同一个线程再次获取这个锁时,计数值将递增,当线程退出同步代码块时,计数值会递减。当计数值为0时,这个锁将释放。
网友评论