产品单页营销型网站模板下载,装修平台哪个口碑最好,太原免费静态网页制作网站,肇庆企业网站关键词优化教程目录
wait() 方法
notify() 方法
notifyAll() 方法
nofity 和 notifyAll
wait 和 notify
wait 和 sleep 的区别
wait 和 join 的区别 由于线程之间是抢占式执行的#xff0c;因此#xff0c;线程之间执行的先后顺序难以预知#xff0c;但是#xff0c;在实际开发中因此线程之间执行的先后顺序难以预知但是在实际开发中有时候我们希望合理的协调多个线程之间的执行先后顺序
例如 在篮球场上每个队员都是独立的 执行流也就是一个 线程 当需要完成一个具体的得分动作时就需要多个队员相互配合按照一定的顺序执行一定的动作线程 1 先向 线程 2 传球线程2 才能 扣篮 要完成 协调工作主要涉及到三个方法 wait()/wait(long timeout)让当前线程进入等待状态 notify()唤醒当前对象上等待的线程 notifyAll()唤醒当前对象上所有等待的线程 接下来我们就来学习这三个方法我们首先来看 wait 方法
wait() 方法
我们先来看一个例子 一个柜子里有食物5个人线程共同使用这个柜子并从里面拿取食物假设同一时间只能一人拿取1号先使用这个柜子对其进行加锁但是当1号打开这个柜子时发现柜子里没有食物此时1号就会关上柜门释放锁等有食物时再来拿 此时其他人就会竞争这个锁争取使用柜子而刚刚释放锁的1号也会参与到锁竞争中因此也就有可能刚刚释放锁的1号又重新拿到锁并且由于1号离得近1号线程处于 RUNNABEL 状态其他线程处于 BLOCKED 状态他就有很大可能再次拿到锁 1号又拿到锁发现没有食物又释放锁又竞争到锁发现没有食物又释放锁...... 如此重复就会导致1号反复获取到锁但是又不能完成实质性的操作而其他线程则无法拿到锁。这种情况称之为 线程饿死线程饥饿 线程饿死这样的情况属于概率性事件1号拿到锁的概率更大但是其他线程也有可能会拿到锁不像 死锁一旦出现后就一定会阻塞但 线程饥饿 这样的情况也极大可能会影响其他线程的运行
因此我们就需要对这种情况进行处理
线程饿死出现的关键在于1号发现自己要执行逻辑的前提条件不具备柜子中没有食物时就应该主动放弃对锁的竞争主动放弃去 CPU 上调度执行即进入阻塞状态一直等待前提条件具备了其他线程往柜子中放了食物此时再解除阻塞参与到锁竞争中
此时就可以使用 wait 进行等待 让 1号 判断前提条件是否满足若不满足则 wait 等待 其他线程让条件满足后再通过 notify 来唤醒 1号 接下来我们就通过具体的代码来学习 wait 的使用 我们让 t1 线程进入等待
public class ThreadDemo18 {public static void main(String[] args) {Object locker new Object();Thread t1 new Thread(() - {System.out.println(t1 进入等待之前);// 进入等待try {locker.wait();} catch (InterruptedException e) {e.printStackTrace();}});t1.start();}
}此时观察运行结果发现抛出了异常 IllegalMonitorStateException 为什么会抛出异常呢 这是因为 wait 必须搭配 synchronized 来使用
wait 做的事情有 1. 使当前执行代码的线程进行等待将线程放到等待队列中 2. 释放当前锁 3. 满足一定条件时被唤醒重新尝试获取到这个锁 wait 要对当前锁进行释放释放锁的前提是要先拿到锁因此 wait 必须放到 synchronized 中使用
public class ThreadDemo18 {public static void main(String[] args) {Object locker new Object();Thread t1 new Thread(() - {System.out.println(t1 进入等待之前);synchronized (locker) {// 进入等待try {locker.wait();} catch (InterruptedException e) {e.printStackTrace();}}});t1.start();}
}wait、sleep 和 join都有可能被 interrupt 提前唤醒都需要处理异常
每个对象里都有一把锁调用 wait 的对象必须和 synchronized 中的锁对象是一致的wait 解除的锁是 locker对象 的锁后续 wait 被唤醒后重新获取到锁当然也是获取到 locker 对象的锁
此时t1 线程就在 wait 这里阻塞了 我们使用 jconsole 来观察 t1 线程的状态 此时线程进入 WAITING 状态
而 wait 结束等待的条件为 1. 其他线程调用该对象的 notify 方法 2. wait 等待时间超时wait 方法提供了带有 timeout 参数的版本用来指定等待最长时间 3. 其他线程调用该等待线程的 interrupt 方法导致 wait 抛出 InterruptedException 异常 接下来我们就来学习 notify() 方法来唤醒等待中的线程 notify() 方法
notify() 方法用于唤醒等待的线程
我们在 t2 线程中唤醒 t1 线程
public class ThreadDemo18 {public static void main(String[] args) {Object locker new Object();Thread t1 new Thread(() - {System.out.println(t1 进入等待之前);synchronized (locker) {// 进入等待try {locker.wait();} catch (InterruptedException e) {e.printStackTrace();}}});Thread t2 new Thread(() - {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}locker.notify();});t1.start();t2.start();}
}此时抛出异常 IllegalMonitorStateException 这是因为 Java 中约定 notify 也需要放到 synchronized 中
public class ThreadDemo18 {public static void main(String[] args) {Object locker new Object();Thread t1 new Thread(() - {System.out.println(t1 进入等待之前);synchronized (locker) {// 进入等待try {locker.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(t1 结束等待);});Thread t2 new Thread(() - {try {Thread.sleep(1000);synchronized (locker) {System.out.println(t2 notify 之前);locker.notify();System.out.println(t2 notify 之后);}} catch (InterruptedException e) {e.printStackTrace();}});t1.start();t2.start();}
}在上述代码中 t1 执行起来后就会立即尝试获取锁拿到锁后就立即打印 t1 进入等待之前并进入 wait 方法释放锁且阻塞等待 t2 执行起来后会先 sleep(1000)保证 t1 能够先拿到锁 t2 sleep 之后t1 处于 WAINTING 状态且锁是释放了的此时t2 就会立即拿到锁 t2 打印 t2 notify 之前执行 notify唤醒 t1此时 t1 就从 WAITING 状态恢复回来 但是由于 t2 还未释放锁t1 WAITING 状态恢复后会尝试获取锁此时会处于阻塞 BLOCKED 状态由于锁竞争引起的 t2 执行完 t2 notify 之后就会释放锁且 t2 执行完毕 此时 t1 的wait 就能够获取到锁并继续执行打印 t1 结束等待 由于我们也可以知道当前线程在执行 notify() 方法后并不会立刻释放锁而是等 synchronized 代码块执行完后才会释放锁
如果有多个线程等待则由线程调度器随机挑选一个处于 wait 状态的线程 notifyAll() 方法
notify() 方法只能唤醒其中一个等待的线程而使用 notifyAll() 方法可以一次唤醒所有等待线程
public class ThreadDemo18 {public static void main(String[] args) {Object locker new Object();Thread t1 new Thread(() - {System.out.println(t1 进入等待之前);synchronized (locker) {// 进入等待try {locker.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(t1 结束等待);});Thread t2 new Thread(() - {try {Thread.sleep(1000);synchronized (locker) {System.out.println(t2 notify 之前);locker.notifyAll();System.out.println(t2 notify 之后);}} catch (InterruptedException e) {e.printStackTrace();}});Thread t3 new Thread(() - {System.out.println(t3 进入等待之前);synchronized (locker) {// 进入等待try {locker.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(t3 结束等待);});t1.start();t2.start();t3.start();}
}我们可以看到notifyAll() 同时唤醒了 t1 和 t3 线程但是虽然同时唤醒了 2 个线程但是这 2 个线程需要竞争锁因此并不是同时执行的而是有先后顺序的执行 nofity 和 notifyAll
notify() 只会唤醒等待队列中的一个线程由线程调度器随机挑选一个处于 wait 状态的线程其他线程仍处于 wait 状态
notifyAll() 则会将等待队列中的线程全都唤醒此时这些线程需要重新竞争锁谁先拿到锁谁后拿到锁也是不确定的 wait 和 notify
wait 和 notify/notifyAll 彼此之间是通过 object 对象联系起来的 若 locker1.wait() locker2.notify() 此时是无法唤醒使用 locker1.wait() 的线程的必须两个对象一致才能唤醒调用 notify 使用的是哪个对象就会唤醒哪个对象 wait 和 sleep 的区别
wait 和 sleep 都能够让线程放弃执行一段时间但wait 是用于线程之间的通信而 sleep 则是让线程阻塞一段时间
wait 和 sleep 都可以被提前唤醒wait 通过 notify 唤醒sleep 通过 interrupt 唤醒但是 使用 wait 时一般都是在不确定要等多少时间的前提下使用的超时时间是用来 兜底 的防止出现 死等 而使用 sleep 是需要知道需要等多少时间的前提下使用的虽然能够提前唤醒但通过异常进行唤醒此时大概率说明程序出现了一些特殊情况 此外
wait() 需要搭配 synchronized 使用但 sleep() 不需要
wait() 是 Object 提供的方法sleep() 是 Thread 提供的静态方法 wait 和 join 的区别
同样的wait 和 join 都能让线程放弃执行一段时间等待其他线程先执行但是wait 是等到 notify 唤醒后解除 wait 状态然后参与到锁竞争中而 join 需要等到其他线程执行完才会继续执行 当一个线程调用 wait 方法时会同步释放锁然后该线程进入等待 状态其他线程会竞争这把锁得到锁的线程继续执行 而一个线程运行过程中调用 另一个线程的 join 方法时当前线程就会停止执行一直等到另一个线程执行完毕才会继续执行 wait() 需要搭配 synchronized 使用但 join() 不需要
wait() 是 Object 提供的方法join() 是 Thread 提供的方法