每次迭代后,线程都会相互等待

时间:2016-01-26 15:30:20

标签: java multithreading loops wait

我希望线程A在for循环中迭代一次,等待线程B循环一遍for for循环并等待线程C循环一遍for循环。然后一遍又一遍。我试着使用wait()和notify(),但我无处可去。怎么能解决这个问题?

public class Test extends Thread {
    static int counter=0;

    synchronized public void run() {
        for(int i=0; i<4; i++) {
            System.out.println(Thread.currentThread().getName()+" "+counter++);
        }
    }


    public static void main(String[] args) throws InterruptedException {
        Test t1 = new Test();
        Test t2 = new Test();
        Test t3 = new Test();

        t1.setName("A");
        t2.setName("B");
        t3.setName("C");

        t1.start();
        t2.start();
        t3.start();

    }
}

它应该将以下内容打印到控制台:

A 0
B 1
C 2
A 3
B 4
C 5
A 6
B 7
C 8
A 9
B 10
C 11

但是使用代码我得到一个像这样的随机输出:

A 0
A 3
A 4
A 5
C 2
C 6
C 7
B 1
C 8
B 9
B 10
B 11

4 个答案:

答案 0 :(得分:0)

由于您正在使实例方法同步,它将尝试获取当前实例的监视器,并且由于所有3个线程都是不同的实例,因此所有获取和释放监视器都是独立的。您应该考虑使用join()等待其他线程。

你可以做类似的事情 -

public class Test extends Thread {
    static int counter=0;
    Test lockTest;

    public Test(){}
    public Test(Test t) {
        this.lockTest = t;
    }


    public void run() {
        for(int i=0; i<4; i++) {
            System.out.println(Thread.currentThread().getName()+" "+counter++);
        }
        if(lockTest != null)
            lockTest.join();   //wait for other thread
    }

    public static void main(String[] args) throws InterruptedException {
       Test t1 = new Test(null);
       Test t2 = new Test(t1);
       Test t3 = new Test(t2);

       t1.setName("A");
       t2.setName("B");
       t3.setName("C");

       t1.start();
       t2.start();
       t3.start();

    }

}

答案 1 :(得分:0)

如果你真的需要这样解决,你可以使用下面的代码。但正如其他人所说,在这种情况下使用线程并没有多大意义。如果你需要等待来自各种任务的计算结果(甚至更好,guava ListenableFuture),我建议调查Future类和ExecutorService.submit。可能是CyclicBarrier,CountDownLatch,某种阻塞队列......很难说不知道你的真正的用例,而不是神秘的代码问题。

public class Test extends Thread {
static int counter=0;

private Test previous;

public void run() {
    for(int i=0; i<4; i++) {
        synchronized(previous) {
            try {
                previous.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName()+" "+counter++);
        synchronized(this) {
            this.notify();
        }
    }
}


public static void main(String[] args) throws InterruptedException {
    Test t1 = new Test();
    Test t2 = new Test();
    Test t3 = new Test();

    t1.setName("A");
    t2.setName("B");
    t3.setName("C");

    t1.previous = t3;
    t2.previous = t1;
    t3.previous = t2;

    t1.start();
    t2.start();
    t3.start();

    synchronized(t3) {
        t3.notify();
    }

}
}

答案 2 :(得分:0)

如果你想按顺序在三个Runnables之间无休止地循环,创建Runnables并将它们提供给单个线程的ThreadPoolExecutor或ExecutorService,如下所示:

while (running)
{
    ExecutorService threadPool = Executors.newSingleThreadExecutor(Executors.defaultThreadFactory());
    threadPool.execute(new RunnableA());
    threadPool.execute(new RunnableB());
    threadPool.execute(new RunnableC());
    threadPool.shutdown();
    threadPool.awaitTermination();//put in a try/catch, omitted for brevity
}

这就是你真正想要的。请注意,上面的ExecutorService具有无限制的队列,因此您必须限制提交任务的速率或使用允许您使用有界队列的ThreadPoolExecutor之类的东西。或者你可以在每个循环中创建一个新的ExecutorService,在每个循环后调用shutdown()然后在循环结束时等待终止。

如果您没有意识到这一点,上面的每个RunnableA / B / C类应该是这样的:

public class RunnableA implements Runnable()
{
    public void run()
    {
        //do stuff, ie, your loop
    }
}

当然总是可以选择将所有三个循环放在一个方法体中,这样做的优点是更简单。

public void doStuff()
{
    //loopA
    //loopB
    //loopC
}

答案 3 :(得分:-1)

您想要一种固定类型的输出,即A X,B X,C X,A X,....并且,您无法修复线程执行的顺序。
 而且,您获得的实际结果取决于您的syncronized关键字。哪个修复了第一个A将被打印然后B,依此类推。
您无法预测线程顺序的执行。