wait、join、sleep、yield的区别

一、wait()

最特殊的wait()方法,为什么把它单独拉出来,因为wait()是Object类的native方法,也就是每一个对象都有的方法,跟线程没有直接关系

使用场景往往是:

public class JoinVsWaitExample {
    public static void main(String[] args) throws InterruptedException {

            // 创建一个用于线程同步的对象
            final Object lock = new Object();

            // 创建另一个线程
            Thread waitThread = new Thread(() -> {
                synchronized (lock) {
                    try {
                        System.out.println("waitThread 进入同步块,准备调用 wait()");
                        // 调用 wait() 方法,线程释放锁并进入等待状态
                        lock.wait();
                        System.out.println("waitThread 被唤醒,继续执行");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });

        waitThread.start();

        // 主线程休眠 1 秒,确保 waitThread 进入等待状态
        Thread.sleep(1000);

        // 主线程获取锁并调用 notify() 方法唤醒等待的线程
        synchronized (lock) {
            System.out.println("主线程调用 notify() 唤醒 waitThread");
            lock.notify();
        }

        System.out.println("主线程继续执行,不等待 waitThread 完成");
    }
}

解释:

首先有一个前提条件:lock是一个Java对象,对象头中有mark word字段来记录锁的持有状态,同时lock也关联了一个监视器对象(Monitor)

1. 从synchronized(lock)开始,waitThread线程就占有了lock对象的Monitor锁(lock的对象头用mark word字段记录了是waitThread在占有锁)

2. 当运行到lock.wait()时,waitThread线程释放了lock的监视器锁,lock关联的监视器中有一个waitSet来收集等待使用lock监视器锁的线程,此时waitThread就加入了waitset()

3. 此时其他线程(本例中为主线程)可以用 synchronized (lock) 获取lock的监视器锁,mark word也会同步更新:lock被主线程占用中

4. 主线程调用lock.notify(),从waitSet中随机选一个线程唤醒,waitSet中目前只有waitThread,因此waitThread线程重新获得lock的锁,可以继续进行

你有没有发现一个问题,既然同一时间只有一个线程可以获取对象的锁,那notifyAll()方法有什么用呢?全部唤醒也只能选其中1个获得锁,跟notify()功能不是一样的吗?

答:对象头中有两个池:entryList锁池和waitSet等待池,线程调用wait()后进入的是waitSet,不竞争锁,主线程调用notify()后,会从waitSet中随机选一个进入entryList锁池,进入后才开始竞争锁。而notifyAll()是唤醒所有waitSet中的线程进入entryList。

也就是说唤醒不代表让等待的线程获得锁,而是仅仅给它一个竞争锁的机会

这里又能引出一个知识点:

进入waitSet的线程是wait或timewait状态,进入entryList的才是阻塞态

二、join()、sleep()、yeild()

2.1 join()

用法:

public class JoinVsWaitExample {
    public static void main(String[] args) throws InterruptedException {

        Thread joinThread = new Thread(() -> {
            try {

                Thread.sleep(2000);
                System.out.println("joinThread 执行完毕");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        // 启动线程
        joinThread.start();

        // 使用 join() 方法,主线程会等待 joinThread 执行完毕
        System.out.println("主线程调用 join() 等待 joinThread");
        joinThread.join();
        System.out.println("主线程继续执行,因为 joinThread 已完成");
    }
}

解释:

1. joinThread线程被创建,之后一直在运行(sleep足足2秒钟,按常理主线程应该早运行完了)

2. 主线程调用join(),实现等待joinThread运行

3. joinThread线程sleep完了,也打印完了,自我销毁

4. 主线程发现joinThread不存在了,正常运行下去,打印并结束

看下join()的源码:

join的实现依靠 while循环+wait(0) ,这样主线程只要发现joinThread还存活,就释放joinThread对象的锁,进入joinThread对象的waitSet,状态变成wait(),CPU就不会再运行主线程因此join()和wait()的区别在:

wait()是释放了业务对象的锁,以此来给别的线程用。

join()是释放了子线程的锁,以此让自己阻塞,等待子线程运行完。而没有释放业务对象的锁

因此说join没有释放锁,指的是没有释放业务对象的锁

2.2 sleep()

sleep是最容易理解的,它只表示什么都不做,不释放锁,线程进timewait,是Thread的静态方法

因此与线程通信没任何关系,因为它不释放锁啊

说说它怎么中断(注意它不能被唤醒,)

2.3 yield()

yield()表示愿意让出CPU资源,Thread的静态方法

不释放任何锁,只是给JVM一个信号,可以把CPU多分配给其他线程

与线程并发编程也没什么直接关系,不能实现线程通信

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值