游戏电视苹果数码历史美丽
投稿投诉
美丽时装
彩妆资讯
历史明星
乐活安卓
数码常识
驾车健康
苹果问答
网络发型
电视车载
室内电影
游戏科学
音乐整形

一文讲尽Thread类的源码精髓

  本文分享自华为云社区《【高并发】Thread类的源码精髓云社区华为云》,作者:冰河。前言
  最近和一个朋友聊天,他跟我说起了他去XXX公司面试的情况,面试官的一个问题把他打懵了!竟然问他:你经常使用Thread创建线程,那你看过Thread类的源码吗?我这个朋友自然是没看过Thread类的源码,然后,就没有然后了!!!
  所以,我们学习技术不仅需要知其然,更需要知其所以然,今天,我们就一起来简单看看Thread类的源码。
  注意:本文是基于JDK1。8来进行分析的。Thread类的继承关系
  我们可以使用下图来表示Thread类的继承关系。
  由上图我们可以看出,Thread类实现了Runnable接口,而Runnable在JDK1。8中被FunctionalInterface注解标记为函数式接口,Runnable接口在JDK1。8中的源代码如下所示。FunctionalInterfacepublicinterfaceRunnable{publicabstractvoidrun();}
  Runnable接口的源码比较简单,只是提供了一个run()方法,这里就不再赘述了。
  接下来,我们再来看看FunctionalInterface注解的源码,如下所示。DocumentedRetention(RetentionPolicy。RUNTIME)Target(ElementType。TYPE)publicinterfaceFunctionalInterface{}
  可以看到,FunctionalInterface注解声明标记在Java类上,并在程序运行时生效。Thread类的源码剖析Thread类定义
  Thread在java。lang包下,Thread类的定义如下所示。publicclassThreadimplementsRunnable{加载本地资源
  打开Thread类后,首先,我们会看到在Thread类的最开始部分,定义了一个静态本地方法registerNatives(),这个方法主要用来注册一些本地系统的资源。并在静态代码块中调用这个本地方法,如下所示。定义registerNatives()本地方法注册系统资源privatestaticnativevoidregisterNatives();static{在静态代码块中调用注册本地系统资源的方法registerNatives();}Thread中的成员变量
  Thread类中的成员变量如下所示。当前线程的名称privatevolatileStringname;线程的优先级privateintpriority;privateThreadthreadQ;privatelongeetop;当前线程是否是单步线程privatebooleansinglestep;当前线程是否在后台运行privatebooleandaemonfalse;Java虚拟机的状态privatebooleanstillbornfalse;真正在线程中执行的任务privateRunnabletarget;当前线程所在的线程组privateThreadGroupgroup;当前线程的类加载器privateClassLoadercontextClassLoader;访问控制上下文privateAccessControlContextinheritedAccessControlContext;为匿名线程生成名称的编号privatestaticintthreadInitNumber;与此线程相关的ThreadLocal,这个Map维护的是ThreadLocal类ThreadLocal。ThreadLocalMapthreadLocalsnull;与此线程相关的ThreadLocalThreadLocal。ThreadLocalMapinheritableThreadLocalsnull;当前线程请求的堆栈大小,如果未指定堆栈大小,则会交给JVM来处理privatelongstackSize;线程终止后存在的JVM私有状态privatelongnativeParkEventPointer;线程的idprivatelongtid;用于生成线程idprivatestaticlongthreadSeqNumber;当前线程的状态,初始化为0,代表当前线程还未启动privatevolatileintthreadStatus0;由(私有)java。util。concurrent。locks。LockSupport。setBlocker设置使用java。util。concurrent。locks。LockSupport。getBlocker访问volatileObjectparkBlocker;Interruptible接口中定义了interrupt方法,用来中断指定的线程privatevolatileInterruptibleblocker;当前线程的内部锁privatefinalObjectblockerLocknewObject();线程拥有的最小优先级publicfinalstaticintMINPRIORITY1;线程拥有的默认优先级publicfinalstaticintNORMPRIORITY5;线程拥有的最大优先级publicfinalstaticintMAXPRIORITY10;
  从Thread类的成员变量,我们可以看出,Thread类本质上不是一个任务,它是一个实实在在的线程对象,在Thread类中拥有一个Runnable类型的成员变量target,而这个target成员变量就是需要在Thread线程对象中执行的任务。线程的状态定义
  在Thread类的内部,定义了一个枚举State,如下所示。publicenumState{初始化状态NEW,可运行状态,此时的可运行包括运行中的状态和就绪状态RUNNABLE,线程阻塞状态BLOCKED,等待状态WAITING,超时等待状态TIMEDWAITING,线程终止状态TERMINATED;}
  这个枚举类中的状态就代表了线程生命周期的各状态。我们可以使用下图来表示线程各个状态之间的转化关系。
  NEW:初始状态,线程被构建,但是还没有调用start()方法。RUNNABLE:可运行状态,可运行状态可以包括:运行中状态和就绪状态。BLOCKED:阻塞状态,处于这个状态的线程需要等待其他线程释放锁或者等待进入synchronized。WAITING:表示等待状态,处于该状态的线程需要等待其他线程对其进行通知或中断等操作,进而进入下一个状态。TIMEWAITING:超时等待状态。可以在一定的时间自行返回。TERMINATED:终止状态,当前线程执行完毕。Thread类的构造方法
  Thread类中的所有构造方法如下所示。publicThread(){init(null,null,ThreadnextThreadNum(),0);}publicThread(Runnabletarget){init(null,target,ThreadnextThreadNum(),0);}Thread(Runnabletarget,AccessControlContextacc){init(null,target,ThreadnextThreadNum(),0,acc,false);}publicThread(ThreadGroupgroup,Runnabletarget){init(group,target,ThreadnextThreadNum(),0);}publicThread(Stringname){init(null,null,name,0);}publicThread(ThreadGroupgroup,Stringname){init(group,null,name,0);}publicThread(Runnabletarget,Stringname){init(null,target,name,0);}publicThread(ThreadGroupgroup,Runnabletarget,Stringname){init(group,target,name,0);}publicThread(ThreadGroupgroup,Runnabletarget,Stringname,longstackSize){init(group,target,name,stackSize);}
  其中,我们最经常使用的就是如下几个构造方法了。publicThread(){init(null,null,ThreadnextThreadNum(),0);}publicThread(Runnabletarget){init(null,target,ThreadnextThreadNum(),0);}publicThread(Stringname){init(null,null,name,0);}publicThread(ThreadGroupgroup,Stringname){init(group,null,name,0);}publicThread(Runnabletarget,Stringname){init(null,target,name,0);}publicThread(ThreadGroupgroup,Runnabletarget,Stringname){init(group,target,name,0);}
  通过Thread类的源码,我们可以看出,Thread类在进行初始化的时候,都是调用的init()方法,接下来,我们看看init()方法是个啥。init()方法privatevoidinit(ThreadGroupg,Runnabletarget,Stringname,longstackSize){init(g,target,name,stackSize,null,true);}privatevoidinit(ThreadGroupg,Runnabletarget,Stringname,longstackSize,AccessControlContextacc,booleaninheritThreadLocals){线程的名称为空,抛出空指针异常if(namenull){thrownewNullPointerException(namecannotbenull);}this。namename;ThreadparentcurrentThread();获取系统安全管理器SecurityManagersecuritySystem。getSecurityManager();线程组为空if(gnull){获取的系统安全管理器不为空if(security!null){从系统安全管理器中获取一个线程分组gsecurity。getThreadGroup();}线程分组为空,则从父线程获取if(gnull){gparent。getThreadGroup();}}检查线程组的访问权限g。checkAccess();检查权限if(security!null){if(isCCLOverridden(getClass())){security。checkPermission(SUBCLASSIMPLEMENTATIONPERMISSION);}}g。addUnstarted();当前线程继承父线程的相关属性this。groupg;this。daemonparent。isDaemon();this。priorityparent。getPriority();if(securitynullisCCLOverridden(parent。getClass()))this。contextClassLoaderparent。getContextClassLoader();elsethis。contextClassLoaderparent。contextClassLoader;this。inheritedAccessControlContextacc!null?acc:AccessController。getContext();this。targettarget;setPriority(priority);if(inheritThreadLocalsparent。inheritableThreadLocals!null)this。inheritableThreadLocalsThreadLocal。createInheritedMap(parent。inheritableThreadLocals);StashthespecifiedstacksizeincasetheVMcaresthis。stackSizestackSize;设置线程idtidnextThreadID();}
  Thread类中的构造方法是被创建Thread线程的线程调用的,此时,调用Thread的构造方法创建线程的线程就是父线程,在init()方法中,新创建的Thread线程会继承父线程的部分属性。run()方法
  既然Thread类实现了Runnable接口,则Thread类就需要实现Runnable接口的run()方法,如下所示。Overridepublicvoidrun(){if(target!null){target。run();}}
  可以看到,Thread类中的run()方法实现非常简单,只是调用了Runnable对象的run()方法。所以,真正的任务是运行在run()方法中的。另外,
  需要注意的是:直接调用Runnable接口的run()方法不会创建新线程来执行任务,如果需要创建新线程执行任务,则需要调用Thread类的start()方法。start()方法publicsynchronizedvoidstart(){线程不是初始化状态,则直接抛出异常if(threadStatus!0)thrownewIllegalThreadStateException();添加当前启动的线程到线程组group。add(this);标记线程是否已经启动booleanstartedfalse;try{调用本地方法启动线程start0();将线程是否启动标记为truestartedtrue;}finally{try{线程未启动成功if(!started){将线程在线程组里标记为启动失败group。threadStartFailed(this);}}catch(Throwableignore){donothing。Ifstart0threwaThrowablethenitwillbepassedupthecallstack}}}privatenativevoidstart0();
  从start()方法的源代码,我们可以看出:
  start()方法使用synchronized关键字修饰,说明start()方法是同步的,它会在启动线程前检查线程的状态,如果不是初始化状态,则直接抛出异常。所以,一个线程只能启动一次,多次启动是会抛出异常的。
  这里,
  也是面试的一个坑:面试官:【问题一】能不能多次调用Thread类的start()方法来启动线程吗?【问题二】多次调用Thread线程的start()方法会发生什么?【问题三】为什么会抛出异常?
  调用start()方法后,新创建的线程就会处于就绪状态(如果没有分配到CPU执行),当有空闲的CPU时,这个线程就会被分配CPU来执行,此时线程的状态为运行状态,JVM会调用线程的run()方法执行任务。sleep()方法
  sleep()方法可以使当前线程休眠,其代码如下所示。本地方法,真正让线程休眠的方法publicstaticnativevoidsleep(longmillis)throwsInterruptedException;publicstaticvoidsleep(longmillis,intnanos)throwsInterruptedException{if(millis0){thrownewIllegalArgumentException(timeoutvalueisnegative);}if(nanos0nanos999999){thrownewIllegalArgumentException(nanosecondtimeoutvalueoutofrange);}if(nanos500000(nanos!0millis0)){millis;}调用本地方法sleep(millis);}
  sleep()方法会让当前线程休眠一定的时间,这个时间通常是毫秒值,这里需要注意的是:
  调用sleep()方法使线程休眠后,线程不会释放相应的锁。join()方法
  join()方法会一直等待线程超时或者终止,代码如下所示。publicfinalsynchronizedvoidjoin(longmillis)throwsInterruptedException{longbaseSystem。currentTimeMillis();longnow0;if(millis0){thrownewIllegalArgumentException(timeoutvalueisnegative);}if(millis0){while(isAlive()){wait(0);}}else{while(isAlive()){longdelaymillisnow;if(delay0){break;}wait(delay);nowSystem。currentTimeMillis()base;}}}publicfinalsynchronizedvoidjoin(longmillis,intnanos)throwsInterruptedException{if(millis0){thrownewIllegalArgumentException(timeoutvalueisnegative);}if(nanos0nanos999999){thrownewIllegalArgumentException(nanosecondtimeoutvalueoutofrange);}if(nanos500000(nanos!0millis0)){millis;}join(millis);}publicfinalvoidjoin()throwsInterruptedException{join(0);}
  join()方法的使用场景往往是启动线程执行任务的线程,调用执行线程的join()方法,等待执行线程执行任务,直到超时或者执行线程终止。interrupt()方法
  interrupt()方法是中断当前线程的方法,它通过设置线程的中断标志位来中断当前线程。此时,如果为线程设置了中断标志位,可能会抛出InteruptedExeption异常,同时,会清除当前线程的中断状态。这种方式中断线程比较安全,它能使正在执行的任务执行能够继续执行完毕,而不像stop()方法那样强制线程关闭。代码如下所示。publicvoidinterrupt(){if(this!Thread。currentThread())checkAccess();synchronized(blockerLock){Interruptiblebblocker;if(b!null){interrupt0();Justtosettheinterruptflagb。interrupt(this);return;}}调用本地方法中断线程interrupt0();}privatenativevoidinterrupt0();总结
  作为技术人员,要知其然,更要知其所以然,我那个朋友技术本身不错,各种框架拿来就用,基本没看过常用的框架源码和JDK中常用的API,属于那种CRUD型程序员,这次面试就栽在了一个简单的Thread类上,所以,大家在学会使用的时候,一定要了解下底层的实现才好啊!
  点击下方,第一时间了解华为云新鲜技术
  华为云博客大数据博客AI博客云计算博客开发者中心华为云
  华为云开发者联盟

艾尔之光新时装豹纹迷彩《艾尔之光》游戏内今日上线新时装豹纹迷彩,结合各时尚元素组合而成的全新装扮,为穿搭达人们带来了更多选择,该时装共有两种颜色,时尚百搭的黑白配色以及个性前卫的豹纹迷彩,无论哪一种……刘国梁重新布局有看头!19岁男队悍将终迎上位之时,林高远太可北京时间12月25日消息,当下时间来到12月下旬。很快,2022年赛季就将画下句号,全新的2023年赛季即将到来,近期围绕明年赛事的讨论热度在不断上升,起步于南非德班的WTT系……你可能涉黄了?大数据扫黄正式启动现如今伴随着互联网络的不断发展,广大老百姓的娱乐生活较之过去也有了很大的丰富。不过值得一提的是,网络并非法外之地,我们在网上的各种行为都会被有关部门监测。一旦出现了不法行为,也……顶风作案!赵薇罪行远不止亲日辱华,资本嘴脸令人发指顶风作案!赵薇罪行远不止亲日辱华,资本嘴脸令人发指赵薇的事情相信很多的人跟小编一样的摸不着头脑,因为到现在为止,官方都没有对赵薇的事情做出一个最终盖棺定论的结局,所以很多……护肝方法被发现,不是洋葱?经常吃一点,肝脏会感谢你如果要是长时间作息不规律和饮食不当的话,会导致肝脏出现一些疾病的发生,但是肝脏前期症状一般都不会有明显的疼痛感。有很多年轻人因为工作原因,晚上长期熬夜去应酬陪客户,可是经常这样……再见了老鹰,科林斯与特雷杨撕破脸!他想被交易至篮网或湖人上赛季还在打东部决赛,这赛季常规赛磕磕绊绊,甚至季后赛都打不进去,这就是现在的亚特兰大老鹰队,球队已经彻底陷入内讧之中!球队的当家球星特雷杨和前锋科林斯两个人的关系非常糟……记者亲测十余款App适老模式不好找,也不好使原标题:您知道App适老模式吗?记者亲测十余款:不好找,也不好使移动互联网时代,对手机App进行适老化改造,有助于让更多老年人跨过数字鸿沟。国务院办公厅曾印发《关于切实解……阔腿裤你穿对了吗?要配对上衣,搭对鞋子,才时髦又显高阔腿裤作为百搭的基础款单品,不管任何季节都能派上用场,对于大多数人而言是必不可少的裤装,即便是在夏天,也有不少人喜欢穿阔腿裤,毕竟穿裙装会有较多的束缚,而裤装便能实现穿搭自由,……对处于叛逆期的孩子我们应该怎么办在孩子的成长过程中,叛逆期是不可避免的。孩子长大成人是一场漫长的旅途,而旅途中会遇到许多重重障碍,在这重重障碍中,叛逆期便是是路上的一座高山。孩子的叛逆期主要有三个阶段,……三国卡牌还能有新花样?快点三国做到了提及三国卡牌,相信绝大部分玩家脑中想起的就是SSR绑架、数值堆砌、无脑战斗等等负面印象。作为手机游戏的最早期类型之一,卡牌游戏尤其是以三国为题材的卡牌游戏。早就已经成为一……澳门特区政府旅游局局长文绮华欢迎广州的朋友们来澳门旅游!自2月6日内地与港澳人员往来全面恢复以来,澳门旅游政策利好不断,赴澳跟团游恢复等措施的实施,令澳门再度成为备受瞩目的旅游目的地。近日,澳门特别行政区政府旅游局局长文绮华率澳门旅……南都书单足不出户的沉浸式文字旅行本期南都书单重点向大家推介一本游记作品女作家大头马的《东游西荡》。《东游西荡》是非典型的旅行文学,更像是作者游荡的私人笔记。作者说:生活是这样的,只要你坚持出门,就一定会有惊喜……
股价波动业绩下滑,海昌海洋公园能否走出IP运营新模式?主题公园是IP价值最集中的表现,除了通过实景体验、娱乐等方式展示主要内容,还可以对IP内容衍生品进行创造,将电影、电视、动画、游戏、乐园、消费品等形式融合在一条完整的产业链之上……首列张石欧中欧班列开行价值2400万元14天后抵达莫斯科燕赵都市报纵览新闻记者李春炜4月6日10:10分,首列张石欧中欧班列从张家口万全西站出发,驶往俄罗斯首都莫斯科。这是张家口开行的首趟中欧班列,不仅开启了张家口与欧洲经贸往……构建全新的酒店第五空间住宿业的空间革命随着生活方式的崛起而不断的变革,住宿业在改革开放四十年的征程里不断创新与发展,和新消费人群的更新与消费理念的变化息息相关。在疫情的大背景下,更是促使酒店等住宿产……想吃晚饭,又怕自己血糖超标?糖友晚饭怎么吃?记住3不2要一日三餐是我们人体血糖最主要的来源,如果日常碳水摄入过多,就容易导致身体血糖指标超标,继而增加糖尿病的隐患。尤其是对于大部分糖友来说,自身血糖已经处于一种非常极限地位,积……开学季换机怎么选?荣耀80SE线下体验后有答案随着开学季的临近,越来越多的网友开始关注起两千价位段机型,毕竟这一价位段机型可供挑选的机型有很多。就以最近热度较高的荣耀80SE来说,就不失为一款不错的机型,恰好我近期也是趁着……2K显示器为啥跟RTX4070Ti最搭?影驰实测解读已经正式上线的RTX4070Ti拥有12GBGDDR6X显存,被认为是2K显示器用户的最佳新选择。RTX4070Ti提供出色的2K游戏性能,在1440p分辨率的Cyber……丰田慌了,电动战略迎来大调整随着全球电动车市场竞争格局变得日益诡谲,全球第一大汽车制造商丰田正在重新考虑其电动汽车战略,以便赶上已经有些落后的进度。重新评估过渡性技术日前,根据《路透社》报道,……赵俊鹏决胜局上演翻盘!掀翻马来西亚一哥,闯入男单八强北京时间8月25日,2022年羽毛球世锦赛在日本东京继续进行,赵俊鹏延续强势闯入八强。面对马来西亚一哥李梓嘉,赵俊鹏经过三局苦战以2比1险胜对手,他成功进入男单八强。本届……福建物构所Sb3掺杂零维铟基卤化物纳米发光材料研究获新进展ns2电子组态离子掺杂全无机非铅金属卤化物因其优异的光学性能和可溶液加工的特点,有望替代铅卤钙钛矿在LED和光电探测等领域发挥重要作用。然而,由于卤素空位等表面缺陷的荧光猝灭效……冬天起床要注意5件事,关乎生命!正确唤醒指南请查收冬天最舒服的事莫过于钻进被窝的那一刻而最痛苦的大概就是起床了你可曾想过为什么一到冬天起床就特别困难呢?真的是太懒了吗?入冬早起困……准妈妈急产分娩,医护接力成功施救三湘都市报新湖南客户端1月25日讯(全媒体记者李琪通讯员张宇璐江芷仪实习生刘杰)快来帮帮我!宝宝的头已经出来了!1月24日,正值大年初三,长沙二胎妈妈王女士一早出现急产,宝宝急……香港意难忘绝对不输北美意难忘,爱恨情仇的拉扯,一步登天的事业王菲和谢霆锋自复合以来首次正大光明的牵手走机场,或许他们一直是正大光明,不能正确看待的可能是吃瓜群众。经过长达24年的爱恨情仇的极限拉扯,这对相差11岁的情侣再次像20年前一样……
友情链接:易事利快生活快传网聚热点七猫云快好知快百科中准网快好找文好找中准网快软网