并发编程:ReentranLock和Condition

时间:2021-06-20 18:00:48

并发编程:ReentranLock和Condition

问题描述

synchronized关键字是JVM层提供的同步机制,我们无需对其异常或它行为做处理(比如释放资源),可以说使用synchronized作同步是最简单但是同时也是比较粗粒度的。JDK1.5开始提供了更细粒度的同步控制,即提供了Lock锁机制和Condition对象,为同步做操作。


ReentranLock就像增强版的Synchronized功能,而Condition则提供了替代Object对象中等待和唤醒方法。 
结合ReentranLock和Condition可以实现比较细粒度的控制并发中的资源共享问题。

Lock介绍和使用

Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,
可以支持多个相关的 Condition 对象。锁是控制多个线程对共享资源进行访问的工具
通常,锁提供了对共享资源的独占访问。一次只能有一个线程获得锁,对共享资源的所有访问都需要首先获得锁。
不过,某些锁可能允许对共享资源并发访问,如 ReadWriteLock 的读取锁。

synchronized关键字介绍

synchronized 方法或语句的使用提供了对与每个对象相关的隐式监视器锁的访问,但却强制所有锁获取和释放均要出现在一个块结构中:当获取了多个锁时,它们必须以相反的顺序释放,且必须在与所有锁被获取时相同的词法范围内释放所有锁。虽然 synchronized 方法和语句的范围机制使得使用监视器锁编程方便了很多,而且还帮助避免了很多涉及到锁的常见编程错误,但有时也需要以更为灵活的方式使用锁。例如,某些遍历并发访问的数据结果的算法要求使用 “hand-over-hand” 或 “chain locking”:获取节点 A 的锁,然后再获取节点 B 的锁,然后释放 A 并获取 C,然后释放 B 并获取 D,依此类推。Lock 接口的实现允许锁在不同的作用范围内获取和释放,并允许以任何顺序获取和释放多个锁,从而支持使用这种技术。

使用Lock注意

随着灵活性的增加,也带来了更多的责任。不使用块结构锁就失去了使用 synchronized 方法和语句时会出现的锁自动释放功能。在大多数情况下,应该使用以下语句:

 Lock l = ...; 
l.lock();
try {
// access the resource protected by this lock
} finally {
l.unlock();
}

备注:通常使用Lock要出现在try.. catch .. finally{} 代码块中,确保最终释放资源。

常用方法

public interface Lock {

/**
* Acquires the lock.
* 获取锁,如果锁不可用,出于线程调度目的,将禁用当前线程,并且在获得锁之前,该线程将一直处于休眠状态。
*/
void lock();

/**
* Acquires the lock only if it is free at the time of invocation.
* 仅在调用时锁为空闲状态才获取该锁。 如果锁可用,则获取锁,并立即返回值 true。
* 如果锁不可用,则此方法将立即返回值 false。 此方法的典型使用语句如下:

Lock lock = ...;
if (lock.tryLock()) {
try {
// manipulate protected state
} finally {
lock.unlock();
}
} else {
// perform alternative actions
}

*/
boolean tryLock();

/**
* Releases the lock.
* 释放锁资源
*/
void unlock();

/**
* Returns a new Condition instance that is bound to this Lock instance.
* 返回绑定到此 Lock 实例的新 Condition 实例。
*/
Condition newCondition();
}

Condition介绍和使用

Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。

常用方法

public interface Condition {

/**
* Causes the current thread to wait until it is signalled or Thread#interrupt interrupted.
* 造成当前线程在接到信号或被中断之前一直处于等待状态。
*/
void await() throws InterruptedException;

/**
* Causes the current thread to wait until it is signalled or interrupted,
* or the specified waiting time elapses. This method is behaviorally
* equivalent to:
* 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态
*/
boolean await(long time, TimeUnit unit) throws InterruptedException;

/**
* Wakes up one waiting thread.
* 唤醒一个等待线程
*/
void signal();

/**
* Wakes up all waiting threads.
* 唤醒所有等待线程
*/
void signalAll();
}

总结说明

可以那么理解:

1、Lock是为了更高效的同步而应运而生的,替代synchronized关键字
2、Condition是为了更灵活的等待唤醒机制,替代了Object的wait()、notify()、notifyAll()方法。


Lock和synchronized区别
Lock 和 synchronized 有一点明显的区别 —— lock 必须在 finally 块中释放。否则,如果受保护的代码将抛出异常,锁就有可能永远得不到释放!这一点区别看起来可能没什么,但是实际上,它极为重要。忘记在 finally 块中释放锁,可能会在程序中留下一个定时炸弹,当有一天炸弹爆炸时,您要花费很大力气才有找到源头在哪。而使用同步,JVM 将确保锁会获得自动释放


参考链接

1、https://www.ibm.com/developerworks/cn/java/j-jtp10264/