Java监视器从私有变量返回错误的值

时间:2014-11-24 11:42:20

标签: java multithreading concurrency synchronized monitor

我在java中实现了一个障碍,当它被一个线程接收时,它会创建一个新对象,该参数的值存储在一个私有变量中,以后再返回。然后,当另一个线程调用屏障时,它使用另一个参数完成前一个对象。第一对顺利进行,其余对接收第一对创建的相同对象。

private Barrier aBarrier;
private boolean first = true;

public synchronized Barrier pairUp(int id){     
    try{
        if(first){
            first = false;

            aBarrier = new Barrier(); aBarrier.setFirst(id);

            wait();
        }
        else{
            first = true;
            aBarrier.setLast(id);

            notify();
        }

    }
    catch(InterruptedException e){System.out.printf("ERROR");}

    return aBarrier;
}

这就是调用上述方法的每个进程的样子。

private int id = ID OF THE PROCESS, 14 RUN CONCURRENTLY SO FROM 0 TO 13 (this is set in the constructor method);
public void run() {
    while(true){
        myBarrier = pairUp(id);
        myBarrier.goThrough();
        //Do stuff that doesn't matter here
        // ....

    }
}

一个Barrier对象包含两个整数和一个稍后会做更多事情的方法。 如果我在调用之前或之后将私有变量aBarrier重置为null,它总是返回null。 我觉得我在这里错过了一些愚蠢的事情。

如果一个进程在第一对之后调用方法pairUp(),它将获得第一个Barrier。 我使用它来区分在pairUp方法中首先出现的进程。

事先谢谢!

1 个答案:

答案 0 :(得分:0)

  

一个Barrier对象包含两个整数和一个稍后会做更多事情的方法。如果我在调用之前或之后将私有变量aBarrier重置为null,它总是返回null。我觉得我在这里错过了一些愚蠢的事情。

我认为问题是你在等待/通知调用之后返回aBarrier ,但它可能已被后续线程更改。将它存储在局部变量中以便不会被更改是关键。

您可能还有多个版本的包装对象,因此您的synchronized语句正在同步另一个对象?

请在下面的代码中注意事项:

  • System.out.println(...)更改同步。这里需要小心。
  • 我使用aBarrier为null或不用来替换first布尔值。

代码:

public class Foo implements Runnable {

    private static int NUM_THREADS = 14;
    private static final AtomicInteger idCounter = new AtomicInteger();
    private static final ExecutorService threadPool = Executors.newFixedThreadPool(NUM_THREADS);
    private static Barrier aBarrier = null;

    public static void main(String[] args) {
        // only 1 Foo object
        Foo foo = new Foo();
        for (int i = 0; i < NUM_THREADS; i++) {
            threadPool.execute(foo);
        }
    }

    public synchronized Barrier pairUp(int id) {
        Barrier barrier = aBarrier;
        try {
            if (barrier == null) {
                barrier = new Barrier();
                barrier.first = id;
                aBarrier = barrier;
                wait();
            } else {
                barrier.last = id;
                aBarrier = null;
                notify();
            }
        } catch (InterruptedException e) {
            // always a good pattern
            Thread.currentThread().interrupt();
            e.printStackTrace();
            barrier = null;
        }
        // return local barrier because aBarrier might have changed
        return barrier;
    }

    @Override
    public void run() {
        int id = idCounter.incrementAndGet();
        while (true) {
            Barrier myBarrier = pairUp(id);
            // System.out.println is synchronized so it may move the bug
            System.out.println(id + ": " + myBarrier.first + " and " + myBarrier.last);
        }
    }

    private static class Barrier {
        int first;
        int last;
    }
}