如果Condition
本身可以单独使用,并且创建代码仅仅是:
final ConditionObject newCondition() {
return new ConditionObject();
}
为什么不是这样创建的?
在类ArrayBlockingQueue
中,构造函数中包含代码:
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
其中notEmpty
和notFull
都是Condition
类的实例。
答案 0 :(得分:1)
摘自Condition
的文档:
Condition
将Object
监视方法(wait
,notify
和notifyAll
)分解为不同的对象,以产生具有多个等待集的效果每个对象,通过使用任意Lock
实现将它们结合起来。Lock
替代了synchronized
方法和语句的使用,而Condition
替代了Object
监视方法的使用。条件(也称为条件队列或条件变量)为一种线程中止执行(“等待”)直到另一线程通知某些状态条件现在为真提供了一种方法。由于对该共享状态信息的访问发生在不同的线程中,因此必须对其进行保护,因此某种形式的锁与该条件相关联。等待条件提供的关键属性是它 atomic 会释放关联的锁并挂起当前线程,就像
Object.wait
一样。
Condition
实例本质上绑定到锁。要获取特定Condition
实例的Lock
实例,请使用其newCondition()
方法。
如前所述,Condition
实例必须与Lock
实例 1 相关联。考虑到这一点,将Lock
用作创建Condition
实例的工厂是很有意义的,因为它暗示了两者之间的关系。可以强制执行此关系的另一种方法是给Condition
一个接受Lock
实例的构造函数,但是由于Condition
也是一个接口,它不能声明构造函数。我还认为,在这种情况下,无参数工厂方法更易于使用。
注意::如果尚不清楚,则ReentrantLock
类是Lock
接口的实现,而ConditionObject
类是实现的Condition
界面。
尝试直接使用ConditionObject
的另一个问题是它是AbstractQueuedSynchronizer
2 的内部类(即非静态嵌套类)。这意味着您将需要后一个类的实例才能创建前一个类的实例。但是,AbstractQueuedSynchronizer
使用的ReentrantLock
的实现是实现细节,并且不会向公众公开。换句话说,您无法调用ConditionObject
的构造函数,这意味着创建实例的唯一方法是通过newCondition()
。
回顾一下,使用工厂方法创建Condition
对象的原因至少有三个:
Lock
和Condition
之间的关系清晰可见。Lock
和Condition
都作为接口,您需要一种将Condition
与Lock
关联的方法,而无需了解实现。否则,将无法"program to an interface"。ConditionObject
是一个内部类,因此不能直接实例化-至少不能通过无法访问该封闭类实例的代码来实例化。 1。 Condition
的方法仅在拥有Lock
的情况下才有意义。就像线程必须在对象上synchronized
才能合法调用该对象的监视方法(即,等待/通知)一样,线程必须拥有关联的Lock
,然后才能合法调用该对象的监视方法。 Condition
(即等待/信号)。
2。还有AbstractQueuedLongSynchronizer
声明了自己的ConditionObject
内部类。尽管该类与AbstractQueuedSynchronizer
声明的类具有相同的名称,但实际上这两个是单独的类。
答案 1 :(得分:1)
“锁和条件”提供了一种以前(1.5版之前)只能通过synchronization
,wait/notify
和自定义条件代码使用的机制。
这是一个惯用的例子
synchronized(foo) { // Lock
while(!conditionMet) // Condition
foo.wait(); // Condition.signalAll();
// Condition met, do something and notify
conditionMet = false;
foo.notifyAll();
}
该条件似乎与锁对象没有任何关系,但可以保证线程安全。如果不访问同步块内部的条件,则无法编写以上代码。 (您可以从非线程安全代码访问条件布尔值并弄乱事情,但总可以弄乱事情。)
整个ReentrantLock
类实际上是Sync
类对象的功能包装,它负责低级同步和条件的创建。条件绑定到特定的Lock
(或Sync
),以基本确保它们是线程安全的。正如Condition
的Javadoc所述
因为访问此共享状态信息的方式不同 线程,必须对其进行保护,因此关联了某种形式的锁 有条件的。
答案 2 :(得分:1)
如Slaw所述,单独实例化一个新的Condition只是没有意义,因为它将失去与原始Lock对象的关联,这类似于从另一个Lock创建一个新Condition(因为java.util.concurrent .locks.Condition不能单独实例化)。
我们可以参考下面的两个示例以了解更多信息:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionTest {
final ReentrantLock lock = new ReentrantLock();
final Condition notEmpty = lock.newCondition();
final Condition notFull = lock.newCondition();
String message;
boolean ready;
boolean isCompleted;
public void consume() {
ReentrantLock lock = this.lock;
lock.lock();
try {
while(!ready)
notEmpty.await();
System.out.println("Received message: " + message);
ready = !ready; // reverse the "ready" state
notFull.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void publish(String message) {
ReentrantLock lock = this.lock;
lock.lock();
try {
while(ready)
notFull.await();
System.out.println("Adding message");
this.message = message;
ready = !ready; // reverse the "ready" state
notEmpty.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
ConditionTest ct = new ConditionTest();
Thread msgProducerThread = new Thread(() -> {
List<String> messages = new ArrayList<String>();
messages.add("Hello!");
messages.add("here we get a message");
messages.add("then we get another message");
messages.forEach(m -> ct.publish(m));
ct.isCompleted = true;
});
Thread msgConsumerThread = new Thread(() -> {
while (!ct.isCompleted)
ct.consume();
});
msgProducerThread.start();
msgConsumerThread.start();
}
}
然后我们将得到以下结果,这表示“锁定”和“条件”功能正常运行:
添加消息
收到的消息:你好
添加消息
收到的消息:当前项目已完成
添加消息
收到的消息:这是对新项目的估算
但是,如果我们尝试通过替换原始代码来使用单独的Condition:
Condition notEmpty = lock.newCondition();
Condition notFull = lock.newCondition();
使用:
ReentrantLock lock2 = new ReentrantLock(fair);
ReentrantLock lock3 = new ReentrantLock(fair);
Condition notEmpty = lock2.newCondition();
Condition notFull = lock3.newCondition();
然后,它将得到:
Adding message
Exception in thread "Thread-0" Received message: Hello!
Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.signal(AbstractQueuedSynchronizer.java:1939)
at ConditionTest.publish(ConditionTest.java:54)
at ConditionTest.lambda$0(ConditionTest.java:75)
at java.lang.Thread.run(Thread.java:745)
java.lang.IllegalMonitorStateException
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.signal(AbstractQueuedSynchronizer.java:1939)
at ConditionTest.consume(ConditionTest.java:33)
at ConditionTest.lambda$1(ConditionTest.java:83)
at java.lang.Thread.run(Thread.java:745)
这意味着Condition和Lock之间没有相关的关联,从而导致抛出IllegalMonitorStateException。