java 为什么等待线程上的interrupt()可以工作,而notify()不能?

yi0zb3m4  于 2023-06-28  发布在  Java
关注(0)|答案(1)|浏览(102)

我已经创建了两个示例类,请忽略像通过get(1L)访问Map这样的怪癖。我以一种非常简单的方式重新创建了我在实际项目中的数据结构,因为我正在努力唤醒线程。
首先是MyThread类。它所做的就是创建一个线程,创建一个Handler示例,并将线程传递给Handler,然后通过join()等待线程。线程所做的就是等待。它是在“this”上同步的,我假设它是线程的示例(匿名内部类)。

public class MyThread {

    public static void main(String[] args) {
        MyThread t = new MyThread();
    }

    public MyThread() {
        Thread t = new Thread() {
            @Override
            public void run() {
                synchronized (this) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        System.out.println("interrupted");
                    }
                }
            }
        };

        Handler handler = new Handler(t);

        System.out.println("starting thread");
        t.start();

        try {
            t.join();
            System.out.println("joined! (woke up)");
        } catch (InterruptedException e) {
            System.out.println("MyThread interrupted");
        }

    }
}

Handler类有一个Map。每个键都有一组指定的线程。3次之后,处理程序应该通过“t.notify()”唤醒MyThread中的线程。我在同一台显示器上同步了,对吧?然而,在它通知MyThread中的线程之后,什么也没有发生。但是如果我将t.notify()改为t.interrupt(),它会中断等待的线程,并将该线程加入MyThread。我不知道为什么会这样,我希望得到一些澄清。

public class Handler {

    NavigableMap<Long, Set<Thread>> map = new TreeMap<>();

    public Handler(Thread t2) {
        System.out.println("in Handler");
        map.put(1L, new HashSet<>());
        map.get(1L).add(t2);

        Thread t = new Thread(new Runnable() {
            int counter = 0;
            @Override
            public void run() {
                try {
                    while (true) {
                        System.out.println("waiting: " + counter);
                        Thread.sleep(3000);
                        if (++counter == 3) {
                            for (Thread t : map.get(1L)) {
                                synchronized (t) {
                                    System.out.println("notifying!");
                                    t.notify();
                                    System.out.println("notified!");
                                }
                            }
                        }
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        t.start();
    }

}

我在这里看到了多个问题,它只是在不同的显示器上同步,但在这里我在“这个”上同步,然后是同一个示例,所以它应该是相同的,不?
请原谅所有的简化(例如无限循环),我只是想创建一个简单的可执行示例。
创建测试示例。我希望notify()会唤醒等待的线程。

bakd9h0s

bakd9h0s1#

对我很有效。您应该删除争用条件。另外,您似乎正在使用相当多的对象,因此您很可能正在等待一个与您正在通知的对象不同的对象。如果唯一的变化是等待的中断,它应该工作。

public class Junk{
    Thread t;
    volatile boolean waiting = false;
    public Junk(){
        t = new Thread(){
            @Override
            public void run(){
                synchronized(this){
                    waiting = true;
                    try{
                        wait();
                    }catch(Exception e){
                        e.printStackTrace();
                    }
                    System.out.println("finished");
                }
            }
        };
        }
    public static void main(String[] args) throws Exception{
        Junk y = new Junk();
        y.t.start();
        while( !y.waiting ){
            System.out.print(".");
        }
        System.out.println("go");
        synchronized(y.t){
            y.t.notify();
        }
        y.t.join();
    }
}

正如注解中所指出的,notify只唤醒一个等待线程,如果我在同一个对象上添加另一个等待,那么notify不会释放其中一个等待线程。

Junk y = new Junk();
new Thread( ()->{
        synchronized(y.t){ try{ y.t.wait();}catch(Exception e){} System.out.println("waited");}
}).start();
y.t.start();

所有线程都通过将notify()切换为notifyAll();来释放
这对于Thread来说尤其是个问题,因为它是非常公开的。至少你应该使用一个不全局用于同步的不同对象。

相关问题