本文共 2566 字,大约阅读时间需要 8 分钟。
Lock
实现提供了比使用 synchronized
方法和语句可获得的更广泛的锁定操作。 锁是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问。一次只能有一个线程获得锁,对共享资源的所有访问都需要首先获得锁。不过,某些锁可能允许对共享资源并发访问,如 的读取锁。
虽然 synchronized
方法和语句的范围机制使得使用监视器锁编程方便了很多,但有时也需要以更为灵活的方式使用锁。
随着灵活性的增加,也带来了更多的责任。不使用块结构锁就失去了使用 synchronized
方法和语句时会出现的锁自动释放功能。在大多数情况下,应该使用以下语句:
Lock l = ...; l.lock(); try { // access the resource protected by this lock } finally { l.unlock(); }
必须谨慎地确保保持锁定时所执行的所有代码用 try-finally 或 try-catch 加以保护,以确保在必要时释放锁。
所有 Lock
实现都必须 实施与内置监视器锁提供的相同内存同步语义,
synchronized
方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。 class X { private final ReentrantLock lock = new ReentrantLock(); // ... public void m() { lock.lock(); // block until condition holds try { // ... method body } finally { lock.unlock() } } }除了实现 接口,此类还定义了
isLocked
和 getLockQueueLength
方法,以及一些相关的 protected
访问方法,这些方法对检测和监视可能很有用。 此锁最多支持同一个线程发起的 2147483648 个递归锁。试图超过此限制会导致由锁方法抛出的 。
Condition
将 Object
监视器方法( 、 和 )分解成截然不同的对象,以便通过将这些对象与任意 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中, Lock
替代了 synchronized
方法和语句的使用, Condition
替代了 Object 监视器方法的使用。 条件(也称为条件队列 或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像 Object.wait
做的那样。
class BoundedBuffer { final Lock lock = new ReentrantLock(); final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100]; int putptr, takeptr, count; public void put(Object x) throws InterruptedException { lock.lock(); try { while (count == items.length) notFull.await(); items[putptr] = x; if (++putptr == items.length) putptr = 0; ++count; notEmpty.signal(); } finally { lock.unlock(); } } public Object take() throws InterruptedException { lock.lock(); try { while (count == 0) notEmpty.await(); Object x = items[takeptr]; if (++takeptr == items.length) takeptr = 0; --count; notFull.signal(); return x; } finally { lock.unlock(); } } }
Condition
实例实质上被绑定到一个锁上。要为特定 实例获得 Condition
实例,请使用其 方法。 | () 返回用于读取操作的锁。 |
| () 返回用于写入操作的锁。 |