Java并发编程:等待唤醒机制

等待通知:wait()/notify()/notifyAll()

这几个方法是Object类中的方法,业务代码中基本不使用。了解即可。

wait()方法

调用该方法的线程会释放共享资源(释放获取的锁),从运行状态进入阻塞状态。wait()方法进行了重载

  • wait():线程进入阻塞状态之后,等待被唤醒。

  • wait(long timeout):进入阻塞状态后,等待一段时间,如果没有被通知唤醒,会自动进入就绪状态。

  • wait(long timeout, int nanos):超时时间为纳秒。

notify()方法

随机唤醒等待队列中等待同一共享资源的1个线程,并使该线程退出等待队列,进入可运行状态。

notifyAll()方法

使所有正在等待队列中等待同一共享资源的全部线程退出等待队列,进入可运行状态。此时优先级最高的那个线程最先执行,但也有可能是随机执行,取决于JVM虚拟机的实现?

相关面试题目

sleep()方法和wait()方法对比

写两个线程,一个线程打印1-52,另一个线程打印A-Z,打印结果为12A34B...5152Z

java 复制代码
class Print {

    private int flag = 1;//信号量。当值为1时打印数字,当值为2时打印字母
    private int count = 1;

    public synchronized void printNum() {
        if (flag != 1) {
            //打印数字
            try {
                // wait使当前线程阻塞,前提是必须获得锁,所以只能在synchronized范围内使用
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.print(2 * count - 1);
        System.out.print(2 * count);
        flag = 2;
        notify();
    }

    public synchronized void printChar() {
        if (flag != 2) {
            //打印字母
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.print((char) (count - 1 + 'A'));
        count++;//当一轮循环打印完之后,计数器加1
        flag = 1;
        notify();
    }
}

public class Test {
    public static void main(String[] args) {
        Print print = new Print();
        new Thread(() -> {
            for (int i = 0; i < 26; i++) {
                print.printNum();
            }
        }).start();
        new Thread(() -> {
            for (int i = 0; i < 26; i++) {
                print.printChar();
            }
        }).start();
    }
}
  • 共同点:两者都可以暂停线程的执行

  • 区别

    • sleep()方法没有释放锁,wait()方法释放了锁

    • wait()通常被用于线程间交互or通信,sleep()通常被用于暂停执行

    • wait()方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的notify()或者notifyAll()方法。sleep方法执行完成后,线程会自动苏醒,或者也可以使用wait(long timeout)超时后线程会自动苏醒

    • sleep()是Thread类的静态本地方法,wait()是Object类的本地方法。

为什么 wait() 方法不定义在 Thread 中?

wait() 是让获得对象锁的线程实现等待,会自动释放当前线程占有的对象锁。每个对象(Object)都拥有对象锁,既然要释放当前线程占有的对象锁并让其进入 WAITING 状态,自然是要操作对应的对象(Object)而非当前的线程(Thread)。

类似的问题:为什么 sleep() 方法定义在 Thread 中?

因为 sleep() 是让当前线程暂停执行,不涉及到对象类,也不需要获得对象锁。

可以直接调用 Thread 类的 run 方法吗?

这是另一个非常经典的 Java 多线程面试问题,而且在面试中会经常被问到。很简单,但是很多人都会答不上来!

new 一个 Thread,线程进入了新建状态。调用 start()方法,会启动一个线程并使线程进入了就绪状态,当分配到时间片后就可以开始运行了。 start() 会执行线程的相应准备工作,然后自动执行 run() 方法的内容,这是真正的多线程工作。 但是,直接执行 run() 方法,会把 run() 方法当成一个 main 线程下的普通方法去执行,并不会在某个线程中执行它,所以这并不是多线程工作。

总结:调用 start() 方法方可启动线程并使线程进入就绪状态,直接执行 run() 方法的话不会以多线程的方式执行。

参考文章

  1. 彻底搞懂Java的等待-通知(wait-notify)机制
  2. 这几种等待唤醒机制【面试必问】你知道吗
  3. JavaGuide面试题
相关推荐
码事漫谈6 小时前
黄仁勋被裁第一天,就收到了橄榄枝
后端
金銀銅鐵7 小时前
带您看懂字节码指令(二):空循环里有什么
java·后端·ai编程
皮皮林5518 小时前
比 Xshell 还好用的 SSH 客户端神器,MobaXterm 太爱了!
后端
我不会起名字3229 小时前
会话缓存失效排查:区分 Redis 失效与节点广播
redis·后端·go·即时通讯·im·故障排查
深入云栈11 小时前
Netty 4.2.x 源码深度解析 (二十三):ByteBuf 核心设计 —— 索引、类型与零拷贝
java·后端·架构
JackJiang11 小时前
IM踩坑:聊天消息ID怎么设计?一个UUID搞不定,我用双ID解决
后端·架构
开心猴爷11 小时前
iOS 上架 App 流程
后端·ios
code_Bo11 小时前
不只命令行:我把跨 Agent 会话同步做成了桌面应用
前端·javascript·后端
聪明的笨猪猪12 小时前
企业知识库AI架构选型:落地导向的架构决策与组件清单
经验分享·面试·架构·embedding·数据库架构
用户83562907805112 小时前
使用 Python 控制 Excel 工作表的分页位置
后端·python