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

苏州网站建设师桔子建站

苏州网站建设师,桔子建站,做盗版电影网站教程,网站维护与建设合同书前言 这篇文章记录了笔者自己对FutureTask的部分源码设计的思考与心得,属于笔者自己的观点,若有哪位热爱源码研究的同仁觉得我说的不对,欢迎批评指正。 提示:在阅读之前必须对FutureTask的源码和实现原理有一定的了解。本文要聊…

前言

这篇文章记录了笔者自己对FutureTask的部分源码设计的思考与心得,属于笔者自己的观点,若有哪位热爱源码研究的同仁觉得我说的不对,欢迎批评指正。

提示:在阅读之前必须对FutureTask的源码和实现原理有一定的了解。本文要聊的内容在FutureTask中awaitDone方法之中。

FutureTask中awaitDone方法的源码

在FutureTask源码中,awaitDone方法的作用是“Awaits completion or aborts on interrupt or timeout.”,即等待任务完成,或者在中断或超时的时候终止。其用于FutureTask的阻塞式获取任务执行结果的get()方法之中,如果在某线程调用FutureTask的get()方法获取任务执行结果,但是任务还没执行完成,则使用awaitDone方法将该线程阻塞。

因为可能不止一个线程去调用get()方法被阻塞,所以FutureTask将所有阻塞等待的线程封装成WaitNode节点,然后使用一个栈来存储这些WaitNode节点。

/*
*  timed:是否定时等待,即等待是否有超时时间
*  nanos:如果是定时等待,此参数是等待的时间
*/
private int awaitDone(boolean timed, long nanos)throws InterruptedException {//如果等待有超时时间,计算等待的截止时间final long deadline = timed ? System.nanoTime() + nanos : 0L;//用于封装等待线程的WaitNode节点WaitNode q = null;//节点是否入栈boolean queued = false;//开启循环for (;;) {//如果线程被中断if (Thread.interrupted()) {//从栈中移除当前线程的WaitNode节点,并且抛出中断异常removeWaiter(q);throw new InterruptedException();}//获取当前任务的状态int s = state;//任务已经结束了(正常完成、抛出异常、被取消)则返回stateif (s > COMPLETING) {if (q != null)q.thread = null;return s;}//如果任务即将完成,让其他线程先执行else if (s == COMPLETING) // cannot time out yetThread.yield();//如果任务没有完成,也不处于即将完成的中将状态COMPLETING,则进入自旋//第一次循环,如果当前WitNode为null,创建一个WaitNode结点else if (q == null)q = new WaitNode();//第二次循环,如果当前WaitNode节点没有入队,则尝试入队else if (!queued)queued = UNSAFE.compareAndSwapObject(this, waitersOffset,q.next = waiters, q);//第三次循环,根据是否有超时时间来挂起线程else if (timed) {//计算当前时间离超时时间点还有的时间差nanos = deadline - System.nanoTime();//超出了指定时间,就移除当前节点并返回任务状态if (nanos <= 0L) {removeWaiter(q);return state;}//挂起线程(有超时时间)LockSupport.parkNanos(this, nanos);}else//挂起线程LockSupport.park(this);}}

用流程图演示心中的疑问

下面,我将awaitDone方法的执行流程画图表示,再谈我先前对awaitDone方法执行流程的疑问。
在这里插入图片描述

在上面的流程图之中,有一点让我最初感觉很奇怪,那就是在执行“创建节点和”和“节点入栈”之后,为什么不接着走下面的流程,而是回到最上面的判断“线程是否中断”这一步骤?这样不是多次一举,去多执行两次无意义的循环吗?

我以前认为,正确的流程应该是这样的。如果判断未创建节点,则先创建节点,然后继续执行“判断节点是否入栈”后续步骤;如果判断节点未入栈,则先执行节点入栈,然后继续执行判断是否定时等待后续步骤。这样的设计似乎让程序更加高效。

下面画出我先前认为更加合理的流程图,红色方框内是变化的内容。
在这里插入图片描述

用自旋锁解释awaitDone方法的流程设计

自旋锁是什么

刚开始,我任务这是JDK源码的缺陷。后来我随着知识面的扩展,我了解到有一种锁叫做“自旋锁”。

先看自旋锁的概念解释:

自旋锁是为高效并发而产生的一种锁优化的思想,要实现线程互斥同步往往需要对某些线程进行阻塞,而线程的挂起和恢复都要转入到内核态去完成。作为Java程序员,笔者也不太懂什么是内核态,但是需要知道的是线程的挂起和恢复是一种重量级的操作,也就是比较消耗CPU性能。
如果共享数据的锁定时间很短,那么为了这很短的时间去进行挂起和恢复线程这个重量级操作就划不来。对于多核CPU来说,可以多个线程并行,让需要请求锁的线程“稍等一下”比直接阻塞要好得多,这样线程大概率可以在等待中获取其他线程释放的锁,就不用放弃CPU的执行时间。
于是产生了自旋锁,自旋锁就是为了让线程等待获取锁而让线程去执行一个有限的循环,这种循环又叫做线程的自旋。

自旋锁思想的体现

从自旋锁思想来看,awaitDone方法中多执行的两次循环并不是无意义的,也不是编码上的缺陷,而是刻意为之。

线程在进入阻塞之前的两次多余的循环,是为了等待任务执行结束而进行的自旋,也就是应用了自旋锁的思想。因为线程的阻塞和重启是比较消耗性能的重量级操作,所以JDK源码中尽量做到能不阻塞线程就不阻塞,故意让线程多进行了两次循环(自旋),为任务执行结束而“稍等一下”,如果两次自旋还等不来任务完成,再阻塞线程

这也是为什么将FutureTask将任务状态细分为COMPLETING(即将结束)和NORMAL(正常结束)的原因,如果任务已经执行完成,但是还没有给返回结果赋值,就将任务状态设为COMPLETING,好让需要获取执行结果的线程知道任务已经快要结束了,可以使用Thread.yield()暂时让出CPU时间片,稍等片刻后就能直接获取到结果而不用阻塞。一切设计都体现了能不阻塞就不阻塞的思想。

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

相关文章:

  • 黑龙江企业网站建设drupal还是wordpress好
  • 厦门做网站多少钱保定手机网站
  • 江门整站优化网站开发的收获体会
  • 如何优化网站性能模板展示网站源码
  • 公司网站模板图片申请建设网站的报告
  • 免费个人网站模版ps软件工程女生好就业吗
  • 网络营销推广方案前言网站优化建设方案
  • wordpress无插件实现网站地图做免费试用的网站
  • 金溪网站建设网站设计师是什么部门
  • 电商网站开发教学视频有限公司和股份公司区别
  • 莆田网站建设创意电子行业网站
  • asp网站和php网站的区别wordpress个人博客实战
  • 模型下载网站开发流程营销qq官网
  • 陕西网站建站同仁seo排名优化培训
  • 成都市建设局官方网站网站建设宣传素材
  • 百度网站建设基本情况环球资源网的网站特色
  • 收费搭建网站中卫网站设计厂家
  • 石家庄seo网站优化wdcp创建网站
  • 接单子做网站做外汇可以参考的网站
  • 几个月网站没有排名网站怎样做全国地区推广
  • 网站首页确认书app制作开发教程
  • 沈阳市建设局网站首页网站 图片防盗链
  • 做黄金的网站网站建设竞争大吗
  • 网站注册和进入asp山东seo网络营销推广
  • 网站优化自已做还是请人做百度热搜的含义
  • 网站建站 优化手机网站cms 开源
  • 美橙互联网站备案平台营销型网站的网址
  • 免费做网站软件下载长沙定制网站开发
  • 深圳做手机网站国外设计网站door
  • 容城县网站开发广东省医院建设协会网站