调用notify不会唤醒其他等待的线程

时间:2013-12-07 10:55:53

标签: java multithreading

我有两个线程,T1和T2。两者都有自己的数组列表,我想从T1中删除数组列表中的一个元素,并且在删除之后,我希望它除非要删除T2数组列表元素

这就是流程应该如何:

t1 remove element
t2 remove element

t1 remove element
t2 remove element
.
.
.

这是我的代码:

import static java.lang.Thread.sleep;
import java.util.ArrayList;

public class ThreadsTest {

    public static void main(String[] args) {
        Thread t1 = new Thread(new T1());
        t1.start();

        Thread t2 = new Thread(new T2());
        t2.start();
    }
}

class T1 implements Runnable {

    private ArrayList<String> list;

    public T1() {
        list = new ArrayList();
        list.add("t1 1");
        list.add("t1 2");
        list.add("t1 3");
        list.add("t1 4");
        list.add("t1 5");
    }

    @Override
    public void run() {
        while (!list.isEmpty()) {
            System.out.println(list.remove(0));
            try {
                synchronized (this) {
                    wait();
                }
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }

}

class T2 implements Runnable {

    private ArrayList<String> list;

    public T2() {
        list = new ArrayList();
        list.add("t2 1");
        list.add("t2 2");
        list.add("t2 3");
        list.add("t2 4");
        list.add("t2 5");
    }

    @Override
    public void run() {
        while (!list.isEmpty()) {
            System.out.println(list.remove(0));
            try {

                synchronized (this) {
                    notifyAll();
                    sleep(1000);
                }

            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }

}

为什么呼叫通知不会唤醒T1?以及如何解决它?

1 个答案:

答案 0 :(得分:2)

您的代码中存在两个问题:

首先你要在监视器上同步/等待“this”,这是实际的对象,所以在T1的情况下它是T1而在T2的情况下它是T2。要正确执行此操作,两个线程都需要同步/等待相同的对象。一种常见的方法是专门创建这样的对象:

final static Object monitor = new Object();

请注意final关键字在此非常重要,您不希望在两者之间意外更改显示器。

但即使你这样做,也无法保证T2在调用notifyAll()期间T1已经在等待,因为线程的执行顺序是未定义的。实际上,这可能导致T1在删除后的最后一次等待调用期间死锁,因为T2已经完成。

修改:如何使用Phaser

执行此操作

声明两个线程都可以使用的Phaser实例:

static final Phaser phaser = new Phaser(2); // 2 threads = 2 parties

T1的运行方法变为:

while (!list.isEmpty()) {
  System.out.println(list.remove(0));
  phaser.arriveAndAwaitAdvance();
  phaser.arriveAndAwaitAdvance();
}
phaser.arriveAndDeregister();

T2的运行方法变为:

while (!list.isEmpty()) {
  phaser.arriveAndAwaitAdvance();
  System.out.println(list.remove(0));
  phaser.arriveAndAwaitAdvance();
}
phaser.arriveAndDeregister();

在这种情况下,取消注册不是必需的,但作为一种安全机制:一旦取消注册,系统就可以在没有完成的线程的情况下继续。