java Wait() / notify() 同步

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17398845/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-01 01:53:03  来源:igfitidea点击:

Wait() / notify() synchronization

javamultithreadingsynchronization

提问by reiley

I'm trying to check how wait/notify works in java.

我正在尝试检查等待/通知在 Java 中的工作方式。

Code:

代码:

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

Output Returned

输出返回

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

I was expecting when notify() is executed the wait will be over & System.out.println("wait over");will get printed. But it seems it only gets printed when tfinished its run().

我期待在执行 notify() 时等待将结束并System.out.println("wait over");打印出来。但它似乎只有在t完成它的run().

回答by MadProgrammer

Object monitor locks need to be performed a single reference of the same lock...

对象监视器锁需要对同一锁的单个引用执行...

In your example you are waitingon an instance of the Thread, but using notifyfrom the Runnable. Instead, you should use a single, common lock object...for example

在您的示例中,您waiting位于 的实例上Thread,但使用notify来自Runnable. 相反,你应该使用一个单一的、通用的锁对象......例如

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

Output...

输出...

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

wait overand leaving run methodcould change positions depending on the thread scheduling.

wait over并且leaving run method可以根据线程调度改变位置。

You could try putting the sleep out side the synchronizedblock. This will release the monitor lock allowing the waitsection to continue running (as it can't start until the lock is released)

你可以试着把睡眠放在synchronized街区之外。这将释放监视器锁,允许该wait部分继续运行(因为在释放锁之前它无法启动)

    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();
                System.out.println("leaving syncronized block");
            }
            try {
                Thread.currentThread().sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("leaving run method");
        }
    }

回答by Pyranja

Answer to updated code :

对更新代码的回答:

From Thread.sleep()javadoc:

来自Thread.sleep()javadoc:

Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision and accuracy of system timers and schedulers. The thread does not lose ownership of any monitors.

使当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,取决于系统计时器和调度程序的精度和准确性。该线程不会失去任何监视器的所有权

If you call Thread.sleep while inside a synchronized block, other threads won't be able to enter the synchronized block. You should never do time consuming tasks while in a synchronized block to avoid this.

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

回答by Petr Pudlák

Note (as others pointed out as well) that you have to use the same object for locking/synchronizing in both threads.

请注意(正如其他人指出的那样),您必须使用相同的对象在两个线程中进行锁定/同步。

If you want your main thread to continue immediately after notifyis called, you have to relinquish the lock temporarily. Otherwise waitwill get called only after the secondary thread leaves the synchronizedblock. And it's never a good idea to keep a lock in a long running computation!

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

One way how to achieve is to use wait(int)on the lock instead of sleep, because waitreleases the synchronization lock temporarily:

一种实现方式是使用wait(int)on 锁而不是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");
        }
    }
}

However, using these low-level primitives can be very error prone and I'd discourage from using them. Instead, I'd suggest you to use Java's high-level primitives for that. For example, you can use CountDownLatchwhich lets one thread wait until other threads count down to zero:

但是,使用这些低级原语很容易出错,我不鼓励使用它们。相反,我建议您为此使用 Java 的高级原语。例如,您可以使用CountDownLatchwhich 让一个线程等待,直到其他线程倒计时为零:

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

Here you don't have to synchronize anything, the latch does everything for you. There are many other primitives you can use - semaphores, an exchanger, thread-safe queues, etc. Explorer the java.util.concurrentpackage.

在这里你不需要同步任何东西,闩锁会为你做一切。您可以使用许多其他原语 - 信号量、交换器、线程安全队列等。浏览该java.util.concurrent包。

Perhaps even better solution is to use even higher level API, such as Akkaprovides. There you work with Actorsor Software transactional memory, which can be composed easily and spare you of most of concurrency issues.

也许更好的解决方案是使用更高级别的 API,例如Akka提供的。在那里您可以使用Actors软件事务性内存,它们可以轻松组合并免除大多数并发问题。