调用通知不会唤醒另一个等待线程 [英] calling notify is not waking up the other waiting thread

查看:53
本文介绍了调用通知不会唤醒另一个等待线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个线程,T1 和 T2.两者都有自己的数组列表,我想从 T1 中删除数组列表的一个元素,在删除后,我希望它一直想要直到 T2 数组列表元素也被删除

I have two threads, T1, and T2. both has its own array list, and I want to remove one element of the array list from T1, and after its removed, I want it to want until T2 arrays list element removed as well

所以流程应该是这样的:

So this is how the flow should be:

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();
            }
        }
    }

}

为什么调用notify不能唤醒T1?以及如何修复它?

Why calling notify does not wake up T1? and how to fix it?

推荐答案

你的代码有两个问题:

首先,您正在同步/等待监视器this",它是实际对象,因此对于 T1,它是 T1,对于 T2,它是 T2.为了正确地做到这一点,两个线程都需要同步/等待 same 对象.一种常见的方法是专门创建这样一个对象:

First you are synchronizing/waiting on the monitor "this", which is the actual object, so in case of T1 it is T1 and for T2 it's T2. To do this properly both threads need to synchronize/wait on the same object. A common way to do this is to specifically create such an object like this:

final static Object monitor = new Object();

请注意,final 关键字在这里很重要,您不希望在两者之间意外更改监视器.

Please note that the final keyword is important here, you do not want to accidentally change the monitor in between.

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

But even if you do this, there is no guarantee that during the time T2 is calling notifyAll() that T1 is already waiting, as the order of execution with threads is undefined. Effectively that can cause T1 to deadlock during the last wait-call after remove, as T2 is already done.

如何使用 移相器

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

Declare a Phaser instance that both threads can use:

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

T1的run方法变为:

The run method of T1 becomes:

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

然后T2的run方法变成:

And the run method of T2 becomes:

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

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

The deregister is not necessary in this case, but acts as a safety mechanism: once deregistered the system can continue without the thread that just finished.

这篇关于调用通知不会唤醒另一个等待线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆