在java中,这是对内在条件队列的错误使用吗?
此程序尝试按顺序打印数字1到10,1个线程打印奇数,第二个线程打印偶数 我一直在读JCIP的书,上面写着: 确保构成条件谓词的状态变量由与条件队列关联的锁保护 在下面的程序中,条件队列将对应于静态成员“obj1”,而构成条件谓词的状态变量是静态易失性成员“count”。(如果我对条件、状态变量、条件谓词的解释有误,请告知我) 下面的程序工作正常,但显然违反了上述习惯用法。我是否理解了作者想说的话?下面的代码真的是一个糟糕的编程实践吗(碰巧工作正常) 你能给我举个例子吗?如果不遵循上面的习惯用法,我会遇到问题在java中,这是对内在条件队列的错误使用吗?,java,multithreading,Java,Multithreading,此程序尝试按顺序打印数字1到10,1个线程打印奇数,第二个线程打印偶数 我一直在读JCIP的书,上面写着: 确保构成条件谓词的状态变量由与条件队列关联的锁保护 在下面的程序中,条件队列将对应于静态成员“obj1”,而构成条件谓词的状态变量是静态易失性成员“count”。(如果我对条件、状态变量、条件谓词的解释有误,请告知我) 下面的程序工作正常,但显然违反了上述习惯用法。我是否理解了作者想说的话?下面的代码真的是一个糟糕的编程实践吗(碰巧工作正常) 你能给我举个例子吗?如果不遵循上面的习惯用法,
public class OddEvenSynchronized implements Runnable {
static Object obj1 = new Object(); // monitor to share data
static volatile int count =1; // condition predicate
boolean isEven;
public OddEvenSynchronized(boolean isEven) { //constructor
this.isEven=isEven;
}
public void run (){
while (count<=10){
if (this.isEven == true){
printEven(); //print an even number
}
else{
printOdd(); //print an odd number
}
}
}
public static void main(String[] args) {
Thread t1 = new Thread (new OddEvenSynchronized(true));
Thread t2 = new Thread (new OddEvenSynchronized(false));
//start the 2 threads
t1.start();
t2.start();
}
void printEven(){
synchronized (obj1) {
while (count%2 != 0){
try{
obj1.wait();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("Even"+count);
count++; //unguarded increment (violation)
synchronized (obj1) {
obj1.notifyAll();
}
} //end method
void printOdd(){
synchronized (obj1) {
while (count%2 == 0){
try{
obj1.wait();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("Odd"+count);
count++; //unguarded increment (violation)
synchronized (obj1) {
obj1.notifyAll();
}
} //end method
} //end class
public类实现可运行{
静态对象obj1=new Object();//用于共享数据的监视器
静态volatile int count=1;//条件谓词
布尔isEven;
公共OddEvenSynchronized(布尔isEven){//构造函数
this.isEven=isEven;
}
公开作废运行(){
而(count如果未在obj1
上同步,则不要读取或写入count
。这是不允许的!打印和增量应在同步块内完成
synchronized (obj1) {
while (count%2 != 0){
try {
obj1.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Even"+count);
}
synchronized (obj1) {
count++;
obj1.notifyAll();
}
您会注意到现在没有理由放弃同步。请将这两个块组合起来
synchronized (obj1) {
while (count%2 != 0){
try {
obj1.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Even"+count);
count++;
obj1.notifyAll();
}
下面的程序工作正常,但显然违反了上述习惯用法
多线程编程的潜在危险在于,有缺陷的程序在大多数情况下似乎都能正常工作。竞争条件可能非常不稳定,因为它们通常需要非常严格的计时条件,而这种情况很少发生
严格遵守规则是非常非常重要的。要正确地进行多线程编程是非常困难的。几乎可以肯定的是,每当你偏离规则并试图变得聪明时,你都会引入一些微妙的错误。这是我能够提出这个问题的唯一原因,正如我与John Kugelman在他的回答中(如果有问题,请更正):
1st Key insight:在Java中,只有一个条件队列与对象的监视器相关联。虽然它们共享条件队列,但条件谓词不同。这种共享会导致不必要的唤醒->检查条件谓词->再次休眠。因此,尽管效率低下,但它们仍会像f如果编码正确,则使用单独的条件队列(而(条件谓词){thread.wait()})
在上面的程序中,条件谓词
计数%2==0
计数%2!=0
它们是不同的,尽管它们是相同条件队列的一部分(即,在此对象的监视器上执行notify()操作将唤醒它们,但一次只能有一个操作)
第二个关键洞察:
volatile count变量确保内存可见性
结论:
一旦我们引入另一个具有相同条件谓词的线程,程序将很容易受到竞争条件的影响(如果不是其他缺陷的话)
另外,请注意,通常wait()notify()机制用于具有相同谓词条件的对象,例如,等待资源锁。上述程序通常用于访谈,我怀疑它在实际代码中是否常见
因此,如果同一条件队列中有两个或多个线程具有不同的条件谓词,并且条件谓词变量是可变的(从而确保内存可见性),然后忽略上面的建议可以生成正确的程序。虽然这没有什么意义,但这确实帮助我更好地理解多线程。为什么它是一个“不”字?这正是我想找出的。如果仔细观察,count是不稳定的,因此不需要将其放入synchronized。我明确地将此程序设置为就是这样。对编辑的回应:在这种情况下,什么样的竞争条件会破坏程序?您的程序可能只需要两个线程就可以了。我还没有发现任何缺陷,至少现在还没有。但是如果引入更多线程,您会遇到问题。保持同步
活动可确保计数
不会发生错误在while
循环结束和随后的打印输出后,或在打印输出之后和增量之前进行更改。严格遵守规则非常重要。不,更重要的是首先理解规则,在遵循规则之前,想象您运行了额外的“偶数”线程。通过适当的同步,这将是好的,可观察到的行为将是相同的。两个偶数线程将相互争斗,每次只有一个线程获胜,这将是好的。但是,对于您的程序,我可以想象各种竞争条件。两个线程可能同时检测并打印偶数,f或实例。这就是您要寻找的响应类型吗?如果是,我会将其添加到我的答案中。没有“内在条件队列”。只有内在锁,没有队列。这就是为什么您必须有一个条件变量和一个采集循环。您能解释一下吗?JCIP说每个对象都有一个内在锁和一个内在条件队列。您引用的引用没有这样说。它说的是“条件队列”,而不是“内在条件队列”。需要更多的上下文,但我怀疑代码本身正在实现一个条件队列。或者,这本书是错误的。如果有一个与锁相关联的条件队列,您就不需要额外的变量或采集循环。从中引用这本书,“一个物体的内在