为什么从同步块调用 `wait` 和 `notify` 方法?

简介: 【8月更文挑战第22天】

在 Java 中,waitnotify 方法是 Object 类提供的用于线程间通信和同步的关键方法。它们必须在 同步块同步方法 中调用,原因如下:

线程安全

同步块或同步方法确保同一时刻只有一个线程可以访问共享资源或代码块。当一个线程调用 wait 方法时,它会释放锁并进入 等待 状态,直到被另一个线程调用 notifynotifyAll 方法唤醒。

如果 waitnotify 方法不在同步块或同步方法中调用,则可能导致 竞争条件。例如,如果一个线程调用 wait 方法后,另一个线程在没有获得锁的情况下修改了共享资源,则第一个线程在被唤醒后可能会使用过时的或不一致的数据。

死锁避免

死锁是指两个或多个线程互相等待对方释放锁的情况,导致所有线程都被阻塞。如果 waitnotify 方法不在同步块或同步方法中调用,则可能导致死锁。

例如,考虑以下代码:

public class DeadlockExample {
   
    private Object lock = new Object();

    public void method1() {
   
        synchronized (lock) {
   
            // 执行任务 1
        }
    }

    public void method2() {
   
        // 没有同步块
        lock.wait();
    }
}

在这个示例中,method1() 是一个同步方法,而 method2() 没有同步块。如果线程 1 调用 method1() 并获得锁,然后线程 2 调用 method2(),则线程 2 将一直等待,因为线程 1 还没有释放锁。同时,线程 1 也无法继续执行,因为它正在等待线程 2 释放锁。这将导致死锁。

最佳实践

为了避免竞争条件和死锁,始终在同步块或同步方法中调用 waitnotify 方法。这将确保在同一时间只有一个线程可以访问共享资源或执行代码块,从而保证线程安全和避免死锁。

示例

以下代码演示了如何正确地在同步块中调用 waitnotify 方法:

public class ThreadCommunication {
   
    private Object lock = new Object();
    private boolean ready = false;

    public void producer() {
   
        synchronized (lock) {
   
            while (!ready) {
   
                try {
   
                    lock.wait();
                } catch (InterruptedException e) {
   
                    // 处理中断
                }
            }

            // 生产数据

            // 唤醒消费者线程
            lock.notify();
        }
    }

    public void consumer() {
   
        synchronized (lock) {
   
            while (!ready) {
   
                try {
   
                    lock.wait();
                } catch (InterruptedException e) {
   
                    // 处理中断
                }
            }

            // 消费数据
        }
    }
}

在这个示例中,生产者线程在 producer() 方法中使用一个同步块来确保在生产数据之前不会被唤醒。消费者线程在 consumer() 方法中也使用一个同步块来确保在数据准备好之前不会被唤醒。这样可以防止竞争条件和死锁。

总结

从同步块或同步方法调用 waitnotify 方法对于确保线程安全和避免死锁至关重要。通过遵循此最佳实践,可以编写健壮且可行的多线程应用程序。

目录
相关文章
|
2月前
|
存储 Java 程序员
优化Java多线程应用:是创建Thread对象直接调用start()方法?还是用个变量调用?
这篇文章探讨了Java中两种创建和启动线程的方法,并分析了它们的区别。作者建议直接调用 `Thread` 对象的 `start()` 方法,而非保持强引用,以避免内存泄漏、简化线程生命周期管理,并减少不必要的线程控制。文章详细解释了这种方法在使用 `ThreadLocal` 时的优势,并提供了代码示例。作者洛小豆,文章来源于稀土掘金。
|
3月前
|
安全 Java 调度
为什么 wait, notify 和 notifyAll 这些方法不在 thread 类里面?
为什么 wait, notify 和 notifyAll 这些方法不在 thread 类里面?
284 0
为什么 wait 方法要在 synchronized 中调用?
它们是在有 synchronized 标记的方法或 synchronized 块中调用的,因为 wait 和 nodify 需要监视对其调用的 Object。 大多数Java开发人员都知道对象类的 wait(),notify() 和 notifyAll() 方法必须在 Java 中的 synchronized 方法或 synchronized 块中调用, 但是我们想过多少次, 为什么在 Java 中 wait, notify 和 notifyAll 来自 synchronized 块或方法?
191 0
为什么 wait 方法要在 synchronized 中调用?
为什么线程协作的 wait() 方法需要写在循环里,你有想过吗?
那么问题是为啥这里是 while 而不是 if 呢?这个问题我最开始也想了很久,按理来说已经在 synchronized 块里面了嘛,就不需要了。这个也是我前面一直是这么认为的,直到最近看了一个 Stackoverflow 上的问题才对这个问题有了比较深入的理解。 试想我们要试想一个有界的队列。那么常见的代码可以是这样:
Zp
wait,notify,notifyAll原理以及实际使用场景
wait,notify,notifyAll原理以及实际使用场景
Zp
230 0
|
Java 调度
join线程执行结束之后,并没有看到哪里有notify方法,请问此时谁去唤醒等待池中的线程
Java中的join方法,阻塞当前线程,直到join线程结束后才继续执行。底层是通过wait来实现的,join线程执行结束之后,并没有看到哪里有notify方法,请问此时谁去唤醒等待池中的线程(join之前的那个“当前”线程)呢?
102 0
join线程执行结束之后,并没有看到哪里有notify方法,请问此时谁去唤醒等待池中的线程
|
调度
【多线程:wait/notify详解】原理及错误用法(虚假唤醒等)
【多线程:wait/notify详解】原理及错误用法(虚假唤醒等)
199 0
|
自动驾驶 小程序 Java
wait/notify/notifyAll方法需不需要被包含在synchronized块中?这是为什么?
wait/notify/notifyAll方法需不需要被包含在synchronized块中?这是为什么?
wait/notify/notifyAll方法需不需要被包含在synchronized块中?这是为什么?
|
存储 Oracle Java
一个线程调用两次 start()方法会出现什么情况?
一个线程调用两次 start()方法会出现什么情况?
201 0
一个线程调用两次 start()方法会出现什么情况?