这是java中内在条件队列的错误使用吗?

时间:2015-08-28 22:27:14

标签: 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

2 个答案:

答案 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()})

在上面的程序中,条件谓词

  1. count%2 == 0
  2. count%2!= 0
  3. 是不同的,虽然它们是相同条件队列的一部分(即对该对象进行notify()监视器将唤醒它们,但是一次只能进行一次)。 / p>

    第二个关键洞察力 volatile计数变量确保内存可见性。

    <强>结论:

    一旦我们引入具有相同条件谓词的另一个线程,程序就会受到竞争条件的影响(如果没有其他缺陷)。

    另外,请注意,对于具有相同条件谓词的对象,通常使用wait()notify()机制,例如,等待资源锁定。上述程序通常用于访谈,我怀疑它是否在现实代码中很常见。

    因此,如果同一条件队列中有两个或多个线程具有不同的条件谓词,并且条件谓词变量是易失性的(因此确保了内存可见性),则忽略上述建议可以生成正确的程序。虽然这没什么意义,但这确实帮助我更好地理解了多线程。