Java Wait()/notify()同步

Java Wait()/notify()同步,java,multithreading,synchronization,Java,Multithreading,Synchronization,我试图检查wait/notify在java中是如何工作的 代码: public class Tester { public static void main(String[] args) { MyRunnable r = new MyRunnable(); Thread t = new Thread(r); t.start(); synchronized (t) { try {

我试图检查wait/notify在java中是如何工作的

代码:

public class Tester {
    public static void main(String[] args) {
        MyRunnable r = new MyRunnable();
        Thread t = new Thread(r);
        t.start();
        synchronized (t) {
            try {
                System.out.println("wating for t to complete");
                t.wait();
                System.out.println("wait over");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class MyRunnable implements Runnable {
    public void run() {
        System.out.println("entering run method");
        synchronized (this) {
            System.out.println("entering syncronised block");
            notify();
            try {
                Thread.currentThread().sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("leaving syncronized block");
        }
        System.out.println("leaving run method");
    }
}
返回的输出

wating for t to complete
entering run method
entering syncronised block
//sleep called
leaving syncronized block
leaving run method
wait over

我希望在执行notify()时等待将结束&
System.out.println(“等待结束”)将被打印。但它似乎只有在
t
完成其
run()

对象监视器锁需要执行同一个锁的单个引用时才会被打印

在您的示例中,您正在
等待
线程的一个实例
,但使用
可运行
中的
通知
。相反,您应该使用单个公共锁对象…例如

public class Tester {

    public static final Object LOCK = new Object();

    public static void main(String[] args) {
        MyRunnable r = new MyRunnable();
        Thread t = new Thread(r);
        t.start();
        synchronized (LOCK) {
            try {
                System.out.println("wating for t to complete");
                LOCK.wait();
                System.out.println("wait over");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static class MyRunnable implements Runnable {

        public void run() {
            System.out.println("entering run method");
            synchronized (LOCK) {
                System.out.println("entering syncronised block");
                LOCK.notify();
                try {
                    Thread.currentThread().sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("leaving syncronized block");
            }
            System.out.println("leaving run method");
        }
    }
}
输出…

wating for t to complete
entering run method
entering syncronised block
leaving syncronized block
wait over
leaving run method
等待
离开运行方法
可能会根据线程调度更改位置

您可以尝试将睡眠置于
synchronized
块之外。这将释放监视器锁,允许
等待
部分继续运行(因为在释放锁之前它无法启动)


对更新代码的答复:

从javadoc:

导致当前正在执行的线程为 指定的毫秒数,取决于系统计时器的精度和准确性 和调度程序线程不会失去任何监视器的所有权

如果在同步块内调用Thread.sleep,其他线程将无法进入同步块。在同步块中,您永远不应该执行耗时的任务,以避免出现这种情况。

请注意(正如其他人所指出的),您必须使用相同的对象来锁定/同步两个线程

如果希望在调用
notify
后立即继续主线程,则必须暂时放弃锁定。否则,只有在次线程离开
synchronized
块后,才会调用
wait
。在长时间运行的计算中保持一个锁从来都不是一个好主意

一种方法是在锁上使用
wait(int)
而不是
sleep
,因为
wait
会临时释放同步锁:

public class Tester {
    private static final Object lock = new Object();

    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable());
        t.start();
        synchronized (lock) {
            try {
                System.out.println("wating for t to complete");
                lock.wait();
                System.out.println("wait over");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    static class MyRunnable implements Runnable {
        public void run() {
            System.out.println("entering run method");
            synchronized (lock) {
                System.out.println("entering syncronised block");
                lock.notify();
                try {
                    lock.wait(1000); // relinquish the lock temporarily
                } catch (InterruptedException ex) {
                    System.out.println("got interrupted");
                }
                System.out.println("leaving syncronized block");
            }
            System.out.println("leaving run method");
        }
    }
}
然而,使用这些低级原语可能非常容易出错,我不鼓励使用它们。相反,我建议您为此使用Java的高级原语。例如,您可以使用
CountDownLatch
,它让一个线程等待其他线程倒计时到零:

import java.util.concurrent.*;

public class TesterC {
    private static final CountDownLatch latch = new CountDownLatch(1);

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

        System.out.println("wating for t to complete");
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("wait over");
    }

    static class MyRunnable implements Runnable {
        public void run() {
            System.out.println("entering run method");
            try {
                latch.countDown();
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                System.out.println("got interrupted");
            }
            System.out.println("leaving run method");
        }
    }
}
在这里,您无需同步任何内容,闩锁为您完成所有操作。您还可以使用许多其他原语-信号量、交换器、线程安全队列等。Explorer是
java.util.concurrent


也许更好的解决方案是使用更高级别的API,如提供的API。在这里,您可以使用或,它可以很容易地组合,并避免大多数并发问题。

您没有在同一个对象上同步MyRunnable。this==r!=t@raul8编辑问题并粘贴答案会使正确答案无效。最好再加一个问题。我知道你正在研究
等待
/
通知
的工作原理。但无论如何,我强烈建议您在代码中使用Java。在低级并发代码中很容易出现难以发现的错误(正如您在本例中所看到的)。谢谢…但我尝试了您的代码。。但它仍然不起作用。有问题的代码updatedFYIThread.sleep(两种形式)是一种静态方法,总是将调用线程置于睡眠状态;因此
Thread.currentThread().sleep(1000)
在语义上是冗余的,并且可能会产生误导(例如,调用
t.sleep(1000)
会使调用线程进入睡眠状态,而不是t)。
import java.util.concurrent.*;

public class TesterC {
    private static final CountDownLatch latch = new CountDownLatch(1);

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

        System.out.println("wating for t to complete");
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("wait over");
    }

    static class MyRunnable implements Runnable {
        public void run() {
            System.out.println("entering run method");
            try {
                latch.countDown();
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                System.out.println("got interrupted");
            }
            System.out.println("leaving run method");
        }
    }
}