例子
例1 最基础的等待-通知
下面一个例子,一个线程"waiting"在同步代码块调用了Object#wait()方法,另一个线程"timedWaiting"调用了Object#wait(3000)等待3000ms,主线程sleep 5000ms后唤醒所有线程。
import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; /** * @see Object#wait(long) 等待对应的毫秒数(不为0)或者被唤醒,执行后续代码 * @see Object#wait() 只有被唤醒(底层源码调用wait 0 ms)才能执行后续代码 * @author rhyme * @date 2020/5/30 11:38 */ @Slf4j public class ObjectWaitMain { private static final Object lock = new Object(); private static final Runnable waiting = () -> { synchronized (lock) { try { log.info("Thread: {}, will Object#wait()", Thread.currentThread().getName()); lock.wait(); log.info("Thread: {} is notified.", Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } }; private static final Runnable timedWaiting = () -> { synchronized (lock) { try { log.info( "Thread: {}, will be notified or wait 3000 milliseconds.", Thread.currentThread().getName()); lock.wait(3000); log.info( "Thread: {}, after being notified or wait 3000 milliseconds.", Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } }; public static void main(String[] args) throws InterruptedException { CompletableFuture.allOf( CompletableFuture.runAsync(waiting), CompletableFuture.runAsync(timedWaiting)); // 主线程sleep5000ms,当3000ms后"timedWaiting"线程执行"wait(3000)"后的代码块 // 如果"timedWaiting"线程在3000ms被notify,那么会立即执行后续代码,不会wait 3000ms TimeUnit.MILLISECONDS.sleep(5000); synchronized (lock) { log.info("main will notifyAll waiting thread."); lock.notifyAll(); } log.info("main end."); } }
执行结果:
例2 Object#wait(long)的参数大于0与等于0
测试类代码
/** * @author rhyme * @date 2020/5/31 0:43 */ @Slf4j public class ThreadPoolExecutorTest { private ThreadPoolExecutor threadPoolExecutor; @Before public void initializeThreadPoolExecutor() { threadPoolExecutor = new ThreadPoolExecutor(4, 8, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>(4)); } @After public void teardown() throws InterruptedException { threadPoolExecutor.shutdown(); TimeUnit.SECONDS.sleep(2); threadPoolExecutor = null; }
大于0
/** * 当 {@link Object#wait(long)}的参数是大于0, 线程wait对象的毫秒数后, 不需被唤醒, 就可再次获取锁执行wait后续代码 * @see Object#wait(long) */ @Test public void synchronizedTimedWaitingTest() { Runnable timedWaiting = () -> { synchronized (this) { while (true) { try { log.info("before Object#wait(1);"); this.wait(1); log.info("after Object#wait(1);"); } catch (InterruptedException e) { e.printStackTrace(); } } } }; threadPoolExecutor.execute(timedWaiting); }
执行结果:
等于0或Object#wait()
/** * 当 {@link Object#wait(long)}的参数是0, 线程只能是被唤醒后, 才能再次获取锁执行wait后续代码 * @see Object#wait() */ @Test public void synchronizedWaitingTest() { Runnable timedWaiting = () -> { synchronized (this) { while (true) { try { log.info("before Object#wait(0);"); // 与"this.wait()"等价 this.wait(0); log.info("after Object#wait(0);"); } catch (InterruptedException e) { e.printStackTrace(); } } } }; threadPoolExecutor.execute(timedWaiting); }
执行结果, 该线程一直wait, 线程池关闭结束, UT结束:
总结
Object#wait(long) 等待对应的毫秒数后(不为0)或者在等待过程中被唤醒后,就能再次获取锁并且获得时间片后执行wait后面代码;
Object#wait() 只有被唤醒后(底层源码调用wait 0 ms)才能再次获取锁执行wait后面代码;
Thread#sleep或TimeUnit#sleep, 除非当前sleep的线程被interrupt, 否则必须sleep对应的时间才能执行后续代码, 并且如果之前获取到锁, 不会释放锁.
Object#wait()方法源码如下:
public final void wait() throws InterruptedException { wait(0); }
public final native void wait(long timeout) throws InterruptedException;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架