当前位置: 首页 > wzjs >正文

pc蛋蛋网站开发windows优化大师官方

pc蛋蛋网站开发,windows优化大师官方,国外 网站 设计,深圳 网页制作在大家的认知里,或者大家也可能搜过,notify唤醒机制到底是随机的呢?还是顺序的呢?在网上其实也有很多人说notify的唤醒机制就是随机的,但实际上并不是这样的,notify的唤醒机制是先进先出的! 目…

          在大家的认知里,或者大家也可能搜过,notify唤醒机制到底是随机的呢?还是顺序的呢?在网上其实也有很多人说notify的唤醒机制就是随机的,但实际上并不是这样的,notify的唤醒机制是先进先出的!

目录

源码注释说明

具体测试用例

测试用例一

测试用例二

底层原理解析

ObjectMonitor 的数据结构

wait源码

notify源码

 DequeueWaiter源码

  源码结论     


源码注释说明

让我们先来看看源码注释是怎么写的

Wakes up a single thread that is waiting on this object's monitor. If any threads are waiting on this object, one of them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation. A thread waits on an object's monitor by calling one of the wait methods.

翻译为:

唤醒正在等待此对象的监视器的单个线程。如果任何线程正在等待此对象,则选择其中一个线程进行唤醒。选择是任意的,由实现自行决定。线程通过调用其中一个 wait 方法等待对象的监视器。

      在注释中其实就已经说明了,选择是任意的,由实现自行决定的,既然注释都这样写了,那我们就先用测试用例测试一下。

具体测试用例

测试用例一

public class Main {private static final Object lock = new Object();private static List<String> awakingOrder = Collections.synchronizedList(new ArrayList<>());private static final int THREAD_COUNT = 100;private static CountDownLatch readyLatch = new CountDownLatch(THREAD_COUNT);private static CountDownLatch startLatch = new CountDownLatch(1);private static CountDownLatch finishLatch = new CountDownLatch(THREAD_COUNT);static class WaitingThread extends Thread {private String name;public WaitingThread(String name) {this.name = name;}@Overridepublic void run() {try {// 先通知主线程该线程已准备就绪readyLatch.countDown();// 等待主线程发出开始信号startLatch.await();synchronized (lock) {System.out.println(name + " 准备等待");lock.wait();awakingOrder.add(name);System.out.println(name + " 被唤醒");}} catch (InterruptedException e) {e.printStackTrace();} finally {finishLatch.countDown();}}}public static void main(String[] args) throws InterruptedException {List<WaitingThread> threads = new ArrayList<>();// 创建并启动100个线程for (int i = 1; i <= THREAD_COUNT; i++) {WaitingThread thread = new WaitingThread("线程" + i);threads.add(thread);thread.start();}// 等待所有线程准备就绪readyLatch.await();System.out.println("所有线程已准备就绪");// 发出开始信号,让所有线程开始等待startLatch.countDown();// 确保所有线程都进入等待状态Thread.sleep(1000);// 依次唤醒所有线程for (int i = 0; i < THREAD_COUNT; i++) {synchronized (lock) {System.out.println("开始第 " + (i + 1) + " 次唤醒");lock.notify();}// 给一点时间让被唤醒的线程执行完毕Thread.sleep(10);}// 等待所有线程执行完毕finishLatch.await();// 输出唤醒顺序System.out.println("唤醒顺序: " + awakingOrder);System.out.println("唤醒的线程总数: " + awakingOrder.size());}
}

       测试用例代码如上,我一开始看的时候还在想HotSpot JVM的的notify唤醒机制原来真的是顺序唤醒的,但是我再仔细看的时候发现并不是这样的!

        当我再往上看的时候发现,原来因为线程并发执行的原因导致并不是按照我原先设置的顺序获取到了synchronized锁!而导致了这个原因,经过对比其实可以发现,线程获取到锁等待的顺序是和被唤醒的顺序是一致的!所以HotSpot JVM的notify实现是先进先出的有序队列。

测试用例二

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;public class Main {private static final Object lock = new Object();private static List<String> awakingOrder = Collections.synchronizedList(new ArrayList<>());private static final int THREAD_COUNT = 100;private static volatile int currentThreadIndex = 0;static class WaitingThread extends Thread {private String name;private int index;private CountDownLatch prevLatch;private CountDownLatch myLatch;public WaitingThread(String name, int index, CountDownLatch prevLatch, CountDownLatch myLatch) {this.name = name;this.index = index;this.prevLatch = prevLatch;this.myLatch = myLatch;}@Overridepublic void run() {try {// 等待前一个线程就绪if (prevLatch != null) {prevLatch.await();}synchronized (lock) {System.out.println(name + " 准备等待,序号: " + index);// 通知下一个线程可以开始等待if (myLatch != null) {myLatch.countDown();}lock.wait();awakingOrder.add(name);System.out.println(name + " 被唤醒,序号: " + index);}} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) throws InterruptedException {List<WaitingThread> threads = new ArrayList<>();List<CountDownLatch> latches = new ArrayList<>();// 创建用于线程间同步的CountDownLatch数组for (int i = 0; i < THREAD_COUNT; i++) {latches.add(new CountDownLatch(1));}// 创建并启动线程for (int i = 0; i < THREAD_COUNT; i++) {CountDownLatch prevLatch = i == 0 ? null : latches.get(i - 1);CountDownLatch myLatch = latches.get(i);WaitingThread thread = new WaitingThread("线程" + (i + 1), i + 1, prevLatch, myLatch);threads.add(thread);thread.start();}// 等待一段时间确保所有线程都进入等待状态Thread.sleep(2000);System.out.println("开始按顺序唤醒线程");// 按顺序唤醒线程for (int i = 0; i < THREAD_COUNT; i++) {synchronized (lock) {System.out.println("准备唤醒第 " + (i + 1) + " 个线程");lock.notify();}// 给足够的时间让被唤醒的线程完成执行Thread.sleep(50);}// 等待所有线程完成for (Thread thread : threads) {thread.join();}// 输出唤醒顺序System.out.println("唤醒顺序: " + awakingOrder);System.out.println("唤醒的线程总数: " + awakingOrder.size());// 验证唤醒顺序是否正确boolean isOrdered = true;for (int i = 0; i < awakingOrder.size(); i++) {String expectedThread = "线程" + (i + 1);if (!awakingOrder.get(i).equals(expectedThread)) {isOrdered = false;break;}}System.out.println("唤醒顺序是否正确: " + isOrdered);}
}

       修改原来的代码 ,为每个线程创建一个 CountDownLatch,每个线程需要等待前一个线程进入等待状态才能继续,使用 prevLatch 和 myLatch 确保线程按顺序进入等待状态,这样就可以顺序的进入锁了,让我们再看下运行结果

 这样其实就可以清晰的看到这个唤醒的结果了,就是先进先出,顺序!

底层原理解析

现在让我们深入分析 HotSpot JVM 中的实现原理

ObjectMonitor 的数据结构

ObjectMonitor 的数据结构在 HotSpot 中,synchronized 的实现依赖于 ObjectMonitor 类,其结构如下:

ObjectMonitor() {_header       = NULL;_count        = 0;_waiters      = 0;_recursions   = 0;_object       = NULL;_owner        = NULL;_WaitSet      = NULL;    // 等待线程队列_WaitSetLock  = 0;_Responsible  = NULL;_succ         = NULL;_cxq          = NULL;FreeNext      = NULL;_EntryList    = NULL;    // 待竞争线程队列_SpinFreq     = 0;_SpinClock    = 0;
}

wait源码

让我们看看具体的wait等待实现

void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {// 将当前线程封装成 ObjectWaiter 对象ObjectWaiter node(THREAD);node.TState = ObjectWaiter::TS_WAIT;// 加入等待队列AddWaiter(&node);// 释放对象锁exit(true, THREAD);// 等待被唤醒Thread::SpinWait(node);
}

notify源码

再看看notify的唤醒实现 

void ObjectMonitor::notify(TRAPS) {// DequeueWaiter 方法会从 _WaitSet 队列头部取出一个等待线程ObjectWaiter* waiter = DequeueWaiter();if (waiter != NULL) {// 将等待线程移动到 EntryListExitExit(waiter);}
}

 DequeueWaiter源码

ObjectWaiter* ObjectMonitor::DequeueWaiter() {// 从 _WaitSet 队列头部获取第一个等待线程ObjectWaiter* waiter = _WaitSet;if (waiter != NULL) {_WaitSet = waiter->_next;waiter->_prev = NULL;waiter->_next = NULL;}return waiter;
}

  源码结论     

         从源码可以看出,wait() 的线程被放入 WaitSet 队列,notify() 总是唤醒 WaitSet 队列中的第一个线程,虽然 Java 规范说 notify 的选择是随机的,但在 HotSpot 实现中实际上是 FIFO(先进先出)的

      运行上面的测试代码,你也会发现,线程被唤醒的顺序与它们进入等待状态的顺序是一致的,多次运行结果都是一样的(在相同的 JVM 实现下),这验证了在 HotSpot JVM 中,notify() 确实是按照 FIFO 顺序唤醒线程的

      但需要注意的是:这个行为是 HotSpot JVM 的具体实现,其他 JVM 可能会有不同的实现

 

http://www.dtcms.com/wzjs/838231.html

相关文章:

  • 嘉定网站建设公司一级a做爰片免费网站中国片
  • 东莞网站建设总结宣武上海网站建设
  • 网站字体一般是什么字体网页制作软件电脑
  • 网站建设百度搜索到左边的图虚拟电脑可以做网站吗
  • 衡水提供网站制作公司哪家专业单页营销网站怎么做
  • 租空间开网站单页面制作
  • 某企业网站建设规划书网站策划与建设
  • 网站服务器开发网页模板网站有那些
  • 网站建设预算报告网站开发环境
  • 新建网站站点的在线音乐网站怎么做
  • 国医堂网站平台建设福田公司
  • 济南网站建设富库网络易语言如何做网站吗
  • 网站 手机 appseo公司推广宣传
  • 上海哪家公司提供专业的网站建设网页设计师是什么专业
  • 网站后台运营怎么做wordpress微信公众号管理
  • 华铭保信建筑公司网站钻石网站建设
  • 学做软件和网站需要知识做网站经常加班还是app
  • 上海网站制作价格辽宁建设工程信息网注册
  • 上海信息科技有限公司软件网站开发揭阳网站设计制作
  • 泉州市建设工程质量监督站网站乐陵外贸seo
  • 大连模板网站制作哪家专业实体店面做网站推广要多少钱
  • 可以做3d电影网站有哪些自己做家具的网站
  • 蓝色系网站设计一站式服务
  • 重庆南坪网站建设公司证书兼职的正规平台哪里有
  • html5高端酒水饮料企业网站模版亚马逊网站建设进度计划书
  • 廊坊模板建站代理环保主题网站模板
  • 做捕鱼网站电话号码wordpress注册评论
  • 手机端网站 优帮云做推广可以上那些网站
  • PC端网站开发以及设计费用贵州专业网站建设公司
  • 如何防止网站被镜像郑州影楼网站建设