多线程06

张开发
2026/4/16 7:46:45 15 分钟阅读

分享文章

多线程06
文章目录前言1. 指令重排序1.1 什么是指令重排序2. wait与notify直观理解演示总结前言通过本文章我们将探讨如下内容指令重排序wait与notify1. 指令重排序1.1 什么是指令重排序在我们买房子的时候一般是先看房再装修最后再住进去。如果出现指令重排序的情况可能就变成了先看房再住进去最后装修。显然这是不合理的。在程序中我们创建一个变量。其执行顺序应该是如下的分配内存地址初始化将这个数值赋给变量在多线程下会出现这样的状况一个变量确实分配到了内存地址但是还没有初始化就被另一个线程拿去用了。也就是说跳过了第二步直接来到了第三步。我们看看如下的代码staticintx0,y0;staticinta0,b0;publicstaticvoidmain(String[]args)throwsInterruptedException{intcount0;while(true){count;xyab0;Threadt1newThread(()-{a1;// ①xb;// ②});Threadt2newThread(()-{b1;// ③ya;// ④});t1.start();t2.start();t1.join();t2.join();if(x0y0){System.out.println(第 count 次出现 (0,0));break;}}}在正常情况下其运行结果应该是这样的线程t1开始运行 a 被赋值为1,x 被赋值为0线程t2开始运行 b被赋值为1y被赋值为1我们看看真实的运行结果我们可以看到 出现了x 0, y 0的情况。我们已经知道x会被赋值为0y会被赋值为1。但这里y却没有被赋值为1。也就是说y a这一句有问题。既然y要为0那么a一定为0。这里就出现了a并没有被赋值为1。出现了我们所说的指令重排序的情况。我们大概就了解到这种程度即可在程序运行中还会有其他原因导致这样的问题。比如线程抢占式运行内存可见性…我们可以用volatile关键字来避免这种情况的发生volatile不仅可以让程序强制访问内存还能防止JVM优化带来的指令重排序问题。只要给变量加上这个关键字修饰即可staticvolatileintx0,y0;staticvolatileinta0,b0;这样后续无论如何都不会再出现x y 0的情况了2. wait与notify直观理解你去餐厅吃饭找到一个空位置坐下你向服务员说点一个套餐服务员听后说“这个套餐现成的售空了若要吃得等厨师有空的时候再做”你给服务员说“我就要吃这个有货的时候通知我我先等一等”你也不好意思一直坐在这个空位置上毕竟现在人很多于是你起来将这个桌子让了出来wait你开始等服务员的通知一段时间后服务员给你发了个消息说“你的套餐已经准备好了”notify),服务员将你的套餐放到一张空桌子上(t2线程释放锁你立刻坐下开始用餐拿到锁执行后面的语句演示我们看看这个代码publicclassdemo6{publicstaticvoidmain(String[]args)throwsRuntimeException{ObjectobnewObject();Threadt1newThread(()-{synchronized(ob){System.out.println(t1线程获取锁ob);try{System.out.println(t1 由于wait释放锁);Thread.sleep(1000);ob.wait();System.out.println(t1 被notify唤醒);}catch(InterruptedExceptione){thrownewRuntimeException(e);}}});Threadt2newThread(()-{try{Thread.sleep(1000);}catch(InterruptedExceptione){thrownewRuntimeException(e);}synchronized(ob){System.out.println(t2 拿到锁);try{System.out.println(t2 5秒后启动notify);Thread.sleep(5000);}catch(InterruptedExceptione){thrownewRuntimeException(e);}ob.notify();try{Thread.sleep(5000);}catch(InterruptedExceptione){thrownewRuntimeException(e);}System.out.println(t2 线程释放锁);}});t1.start();t2.start();}}我来解释一下首先t1拿到锁ob一秒后通过wait()立刻释放锁(ob),t2拿到由t1释放的锁(ob),t2五秒后使用notify尝试唤醒wait五秒后t2释放锁t2释放锁后t1立即获取锁输出t1 被notify唤醒。这里要注意一下当线程t2使用notify后t1线程并不会立刻获取锁。原因在于虽然t2使用了notify但notify并不会使得t2立刻释放锁而是要等到t2线程正常运行完毕释放锁t1才能拿到这个锁继续运行。关于wait其实也可以加入参数进去这个和sleep()一样直接传数字即可。其含义是最多等待多久。关于notify还有一个notifyAll其作用是一次性唤醒当前所有因为wait休眠的线程这里不做过多的讨论。总结通过本文我们学习了以下核心内容指令重排序JVM为了优化性能可能会对指令执行顺序进行重排在多线程环境下可能导致非预期结果。可以通过volatile关键字来禁止指令重排序保证内存可见性。wait 与 notify 机制wait()会使当前线程释放锁并进入等待状态notify()用于唤醒等待中的线程。需要注意的是notify()不会立即释放锁线程需执行完毕后才释放。扩展知识wait()支持传入超时参数notifyAll()可以一次性唤醒所有等待线程适用于更复杂的并发场景。

更多文章