前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java 多线程系列Ⅲ

Java 多线程系列Ⅲ

作者头像
终有救赎
发布2024-02-01 10:35:03
730
发布2024-02-01 10:35:03
举报
文章被收录于专栏:多线程多线程

一、初识 wait、notify、notifyAll

wait()notify()方法是用于处理多线程同步的关键方法之一。它们通常用于协调多个线程对共享资源的访问和修改。

有一个共享的缓冲区,生产者负责往缓冲区中添加数据,消费者负责从缓冲区中取出数据。当缓冲区已满时,生产者需要等待消费者从缓冲区中取出一些数据后再继续添加;当缓冲区为空时,消费者需要等待生产者往缓冲区中添加一些数据后再继续取出。

定义一个名为SharedBuffer的类,它代表共享的缓冲区。这个类有两个属性:一个是整型的bufferSize,表示缓冲区的大小;另一个是队列queue,用于存储数据。它还有两个方法:一个是put(),用于生产者往缓冲区中添加数据;另一个是get(),用于消费者从缓冲区中取出数据。

代码语言:javascript
复制
import java.util.LinkedList;  
import java.util.Queue;  
  
public class SharedBuffer {  
    private int bufferSize;  
    private Queue<Integer> queue;  
  
    public SharedBuffer(int bufferSize) {  
        this.bufferSize = bufferSize;  
        this.queue = new LinkedList<>();  
    }  
  
    public synchronized void put(int data) throws InterruptedException {  
        while (queue.size() == bufferSize) {  
            // 当缓冲区已满时,调用wait()方法使当前线程等待,并释放对象锁  
            wait();  
        }  
        queue.offer(data);  
        // 唤醒正在等待从缓冲区中取数据的消费者线程  
        notifyAll();  
    }  
  
    public synchronized Integer get() throws InterruptedException {  
        while (queue.isEmpty()) {  
            // 当缓冲区为空时,调用wait()方法使当前线程等待,并释放对象锁  
            wait();  
        }  
        Integer data = queue.poll();  
        // 唤醒正在等待往缓冲区中添加数据的生产者线程  
        notifyAll();  
        return data;  
    }  
}

当生产者调用put()方法往缓冲区中添加数据时,如果缓冲区已满,它会调用wait()方法使当前线程等待,并释放对象锁。当消费者从缓冲区中取出一些数据后,它会调用notifyAll()方法唤醒正在等待的生产者线程。同样地,当消费者调用get()方法从缓冲区中取出数据时,如果缓冲区为空,它也会调用wait()方法使当前线程等待,并在生产者往缓冲区中添加一些数据后通过调用notifyAll()方法唤醒消费者线程。这样,生产者和消费者就可以在多线程环境下安全地共享缓冲区了。需要注意的是,在这个例子中,我们使用了synchronized关键字来确保同一时刻只有一个线程可以执行put()get()方法。这是因为这两个方法都需要访问和修改共享资源(即缓冲区),所以需要进行同步处理以避免数据竞争和不一致的问题。

二、wait、notify、notifyAll 功能介绍

wait()

Wait() 方法使当前线程等待,并释放对象锁,直到其他线程调用该对象的notify()或notifyAll()方法唤醒该线程。它通常用于让当前线程等待,直到共享资源可用或满足某些条件。使用wait()方法时,需要先获取对象锁,否则会抛出IllegalMonitorStateException异常。

wait等待结束条件:

  1. 其他线程调用该对象的 notify 方法
  2. wait 等待时间超时 (wait 方法提供一个带有 timeout 参数的版本, 来指定等待时间)
  3. 其他线程调用该等待线程的 interrupted 方法, 导致 wait 抛出 InterruptedException 异常

notify()

Notify() 方法唤醒在该对象上等待的单个线程。它通常用于通知等待该对象的单个线程,共享资源已经可用或满足某些条件。使用notify()方法时,需要先获取对象锁,否则会抛出IllegalMonitorStateException异常。

notify注意事项:

  1. 方法notify()也要在同步方法或同步块中调用,该方法是用来通知那些可能等待该对象的对象锁的其它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。
  2. 如果有多个线程等待,则有线程调度器随机挑选出一个呈 wait 状态的线程。(并没有 “先来后到”)
  3. 在notify()方法后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行 完,也就是退出同步代码块之后才会释放对象锁。

notify all()

Notify All() 方法唤醒在该对象上等待的所有线程。它通常用于通知等待该对象的所有线程,共享资源已经可用或满足某些条件。使用notifyAll()方法时,需要先获取对象锁,否则会抛出IllegalMonitorStateException异常。

notifyAll注意事项:

虽然是同时唤醒 多个线程, 但是这 多个线程需要竞争锁,所以并不是同时执行,而仍然是先获取锁的先执行。

wait、notify、notifyAll 要点总结

  1. 必须在同步方法或同步块中使用wait()、notify()或notifyAll()方法,否则会抛出IllegalMonitorStateException异常。
  2. wait()、notify()和notifyAll()方法是Object类的final方法,因此不能被重写。
  3. wait()、notify()和notifyAll()方法必须与synchronized关键字一起使用,以确保线程安全。
  4. 调用wait()、notify()或notifyAll()方法的线程必须拥有当前对象的锁,否则会抛出IllegalMonitorStateException异常。
  5. 应该避免在循环中反复调用wait()方法,因为这可能会导致死锁。应该使用带有条件的wait循环,以便在满足条件时退出等待。
  6. 应该小心使用notify()和notifyAll()方法,以避免意外的唤醒所有线程而没有处理异常情况。应该在满足条件时再调用这些方法。
  7. 在使用wait()、notify()和notifyAll()方法时,应该考虑使用Java的Lock和Condition接口,以提高灵活性和可读性。

wait/notify 使用示例

代码语言:javascript
复制
public class SharedResource {  
    private int count = 0;  
    private Object lock = new Object();  
  
    public void increment() {  
        synchronized (lock) {  
            count++;  
            System.out.println("Count is: " + count);  
            lock.notify(); // 唤醒一个等待线程  
        }  
    }  
  
    public void decrement() throws InterruptedException {  
        synchronized (lock) {  
            count--;  
            System.out.println("Count is: " + count);  
            lock.wait(); // 当前线程等待,直到其他线程调用notify或notifyAll唤醒  
        }  
    }  
}

有一个共享资源类SharedResource,其中有一个计数器count和一个锁对象lockincrement()方法会增加计数器的值,并使用notify()方法唤醒一个等待线程。decrement()方法会减少计数器的值,并使用wait()方法使当前线程等待,直到其他线程调用notify()notifyAll()方法唤醒。

三、wait、join、sleep 归纳

(1)wait wait()方法是 Object类 提供的实例方法,可以使线程进入等待状态,直到其他线程调用了该对象的notify()或notifyAll()方法。通常被用于线程间通信,如生产者-消费者模式中(后续介绍),消费者需要等待生产者通知有新数据可取。当线程调用 wait() 方法时,它会释放占据的锁,并且线程的状态为WAITING,直到notify()或notifyAll()方法被调用。

(2)join join() 方法是 Thread类 提供的方法静态方法,用于等待被调用线程执行完毕。在主线程中调用了子线程的join() 方法后,主线程会进入 WAITING 状态,直到子线程执行完毕才会继续执行。可以用来保证多个线程按照指定顺序执行。

(3)sleep sleep()方法也是 Thread类 提供的实例方法,它可以使当前线程暂停执行一段时间。当线程调用 sleep() 方法时,它不会释放锁,线程的状态为 TIMED_WAITING 。通常被用于控制程序执行的速度或时间,或常常在循环内部以等待某些条件的发生。

wait() 方法是用于线程间的通信,join() 方法是用于等待其他线程执行完毕,sleep() 方法是用于暂停当前线程的执行。在使用上, wait 需要搭配 synchronized 使用,sleep 和 join 则不需要。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-02-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、初识 wait、notify、notifyAll
  • 二、wait、notify、notifyAll 功能介绍
    • wait()
      • notify()
        • notify all()
          • wait、notify、notifyAll 要点总结
            • wait/notify 使用示例
            • 三、wait、join、sleep 归纳
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档