此程序尝试以顺序方式打印数字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
答案 0 :(得分:1)
如果您未count
同步,请勿阅读或写信obj1
。那不是不!打印和增量应该从内部同步块中完成。
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();
}
以下程序正常运行,但显然违反了上述习惯法。
多线程编程的潜在危险在于,错误的程序可以出现以在大多数情况下正常工作。竞争条件可能非常狡猾,因为它们通常需要非常严格的时间条件,这很少发生。
遵守规则是非常非常重要的。获得多线程编程非常困难。几乎可以肯定的是,只要你偏离规则并试图变得聪明,你就会引入微妙的错误。
答案 1 :(得分:0)
我能够提出这个问题的唯一原因,正如我与约翰库格曼在答案中的讨论暗示的那样(如果出现问题请纠正):
第一个关键洞察力:在Java中,只有一个条件队列与对象的监视器相关联。虽然它们共享条件队列,但条件谓词是不同的。这种共享导致不必要的唤醒 - &gt;检查条件谓词 - &gt;再次入睡。因此,尽管效率低,但如果编码正确,它们仍将表现为一种单独的条件队列(while(条件谓词){thread.wait()})
在上面的程序中,条件谓词
是不同的,虽然它们是相同条件队列的一部分(即对该对象进行notify()监视器将唤醒它们,但是一次只能进行一次)。 / p>
第二个关键洞察力 volatile计数变量确保内存可见性。
<强>结论:强>
一旦我们引入具有相同条件谓词的另一个线程,程序就会受到竞争条件的影响(如果没有其他缺陷)。
另外,请注意,对于具有相同条件谓词的对象,通常使用wait()notify()机制,例如,等待资源锁定。上述程序通常用于访谈,我怀疑它是否在现实代码中很常见。
因此,如果同一条件队列中有两个或多个线程具有不同的条件谓词,并且条件谓词变量是易失性的(因此确保了内存可见性),则忽略上述建议可以生成正确的程序。虽然这没什么意义,但这确实帮助我更好地理解了多线程。