范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

一文讲尽Thread类的源码精髓

  本文分享自华为云社区《【高并发】Thread类的源码精髓-云社区-华为云》,作者:冰 河。前言
  最近和一个朋友聊天,他跟我说起了他去XXX公司面试的情况,面试官的一个问题把他打懵了!竟然问他:你经常使用Thread创建线程,那你看过Thread类的源码吗?我这个朋友自然是没看过Thread类的源码,然后,就没有然后了!!!
  所以,我们学习技术不仅需要知其然,更需要知其所以然,今天,我们就一起来简单看看Thread类的源码。
  注意:本文是基于JDK 1.8来进行分析的。Thread类的继承关系
  我们可以使用下图来表示Thread类的继承关系。
  由上图我们可以看出,Thread类实现了Runnable接口,而Runnable在JDK 1.8中被@FunctionalInterface注解标记为函数式接口,Runnable接口在JDK 1.8中的源代码如下所示。@FunctionalInterface public interface Runnable {     public abstract void run(); }
  Runnable接口的源码比较简单,只是提供了一个run()方法,这里就不再赘述了。
  接下来,我们再来看看@FunctionalInterface注解的源码,如下所示。@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface FunctionalInterface {}
  可以看到,@FunctionalInterface注解声明标记在Java类上,并在程序运行时生效。Thread类的源码剖析Thread类定义
  Thread在java.lang包下,Thread类的定义如下所示。public class Thread implements Runnable { 加载本地资源
  打开Thread类后,首先,我们会看到在Thread类的最开始部分,定义了一个静态本地方法registerNatives(),这个方法主要用来注册一些本地系统的资源。并在静态代码块中调用这个本地方法,如下所示。//定义registerNatives()本地方法注册系统资源 private static native void registerNatives(); static {     //在静态代码块中调用注册本地系统资源的方法     registerNatives(); } Thread中的成员变量
  Thread类中的成员变量如下所示。//当前线程的名称 private volatile String name; //线程的优先级 private int            priority; private Thread         threadQ; private long           eetop; //当前线程是否是单步线程 private boolean     single_step; //当前线程是否在后台运行 private boolean     daemon = false; //Java虚拟机的状态 private boolean     stillborn = false; //真正在线程中执行的任务 private Runnable target; //当前线程所在的线程组 private ThreadGroup group; //当前线程的类加载器 private ClassLoader contextClassLoader; //访问控制上下文 private AccessControlContext inheritedAccessControlContext; //为匿名线程生成名称的编号 private static int threadInitNumber; //与此线程相关的ThreadLocal,这个Map维护的是ThreadLocal类 ThreadLocal.ThreadLocalMap threadLocals = null; //与此线程相关的ThreadLocal ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; //当前线程请求的堆栈大小,如果未指定堆栈大小,则会交给JVM来处理 private long stackSize; //线程终止后存在的JVM私有状态 private long nativeParkEventPointer; //线程的id private long tid; //用于生成线程id private static long threadSeqNumber; //当前线程的状态,初始化为0,代表当前线程还未启动 private volatile int threadStatus = 0; //由(私有)java.util.concurrent.locks.LockSupport.setBlocker设置 //使用java.util.concurrent.locks.LockSupport.getBlocker访问 volatile Object parkBlocker; //Interruptible接口中定义了interrupt方法,用来中断指定的线程 private volatile Interruptible blocker; //当前线程的内部锁 private final Object blockerLock = new Object(); //线程拥有的最小优先级 public final static int MIN_PRIORITY = 1; //线程拥有的默认优先级 public final static int NORM_PRIORITY = 5; //线程拥有的最大优先级 public final static int MAX_PRIORITY = 10;
  从Thread类的成员变量,我们可以看出,Thread类本质上不是一个任务,它是一个实实在在的线程对象,在Thread类中拥有一个Runnable类型的成员变量target,而这个target成员变量就是需要在Thread线程对象中执行的任务。线程的状态定义
  在Thread类的内部,定义了一个枚举State,如下所示。public enum State {    //初始化状态     NEW,    //可运行状态,此时的可运行包括运行中的状态和就绪状态     RUNNABLE,    //线程阻塞状态     BLOCKED,    //等待状态     WAITING,     //超时等待状态     TIMED_WAITING,     //线程终止状态     TERMINATED; }
  这个枚举类中的状态就代表了线程生命周期的各状态。我们可以使用下图来表示线程各个状态之间的转化关系。
  NEW:初始状态,线程被构建,但是还没有调用start()方法。RUNNABLE:可运行状态,可运行状态可以包括:运行中状态和就绪状态。BLOCKED:阻塞状态,处于这个状态的线程需要等待其他线程释放锁或者等待进入synchronized。WAITING:表示等待状态,处于该状态的线程需要等待其他线程对其进行通知或中断等操作,进而进入下一个状态。TIME_WAITING:超时等待状态。可以在一定的时间自行返回。TERMINATED:终止状态,当前线程执行完毕。Thread类的构造方法
  Thread类中的所有构造方法如下所示。public Thread() {     init(null, null, "Thread-" + nextThreadNum(), 0); } public Thread(Runnable target) {     init(null, target, "Thread-" + nextThreadNum(), 0); } Thread(Runnable target, AccessControlContext acc) {     init(null, target, "Thread-" + nextThreadNum(), 0, acc, false); } public Thread(ThreadGroup group, Runnable target) {     init(group, target, "Thread-" + nextThreadNum(), 0); } public Thread(String name) {     init(null, null, name, 0); } public Thread(ThreadGroup group, String name) {     init(group, null, name, 0); } public Thread(Runnable target, String name) {     init(null, target, name, 0); } public Thread(ThreadGroup group, Runnable target, String name) {     init(group, target, name, 0); } public Thread(ThreadGroup group, Runnable target, String name,               long stackSize) {     init(group, target, name, stackSize); }
  其中,我们最经常使用的就是如下几个构造方法了。public Thread() {     init(null, null, "Thread-" + nextThreadNum(), 0); } public Thread(Runnable target) {     init(null, target, "Thread-" + nextThreadNum(), 0); } public Thread(String name) {     init(null, null, name, 0); } public Thread(ThreadGroup group, String name) {     init(group, null, name, 0); } public Thread(Runnable target, String name) {     init(null, target, name, 0); } public Thread(ThreadGroup group, Runnable target, String name) {     init(group, target, name, 0); }
  通过Thread类的源码,我们可以看出,Thread类在进行初始化的时候,都是调用的init()方法,接下来,我们看看init()方法是个啥。init()方法private void init(ThreadGroup g, Runnable target, String name, long stackSize) {     init(g, target, name, stackSize, null, true); } private void init(ThreadGroup g, Runnable target, String name,                   long stackSize, AccessControlContext acc,                   boolean inheritThreadLocals) {     //线程的名称为空,抛出空指针异常     if (name == null) {         throw new NullPointerException("name cannot be null");     }      this.name = name;     Thread parent = currentThread();     //获取系统安全管理器     SecurityManager security = System.getSecurityManager();     //线程组为空     if (g == null) {         //获取的系统安全管理器不为空         if (security != null) {             //从系统安全管理器中获取一个线程分组             g = security.getThreadGroup();         }         //线程分组为空,则从父线程获取         if (g == null) {             g = parent.getThreadGroup();         }     }     //检查线程组的访问权限     g.checkAccess();     //检查权限     if (security != null) {         if (isCCLOverridden(getClass())) {             security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);         }     }     g.addUnstarted();     //当前线程继承父线程的相关属性     this.group = g;     this.daemon = parent.isDaemon();     this.priority = parent.getPriority();     if (security == null || isCCLOverridden(parent.getClass()))         this.contextClassLoader = parent.getContextClassLoader();     else         this.contextClassLoader = parent.contextClassLoader;     this.inheritedAccessControlContext =         acc != null ? acc : AccessController.getContext();     this.target = target;     setPriority(priority);     if (inheritThreadLocals && parent.inheritableThreadLocals != null)         this.inheritableThreadLocals =         ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);     /* Stash the specified stack size in case the VM cares */     this.stackSize = stackSize;      //设置线程id     tid = nextThreadID(); }
  Thread类中的构造方法是被创建Thread线程的线程调用的,此时,调用Thread的构造方法创建线程的线程就是父线程,在init()方法中,新创建的Thread线程会继承父线程的部分属性。run()方法
  既然Thread类实现了Runnable接口,则Thread类就需要实现Runnable接口的run()方法,如下所示。@Override public void run() {     if (target != null) {         target.run();     } }
  可以看到,Thread类中的run()方法实现非常简单,只是调用了Runnable对象的run()方法。所以,真正的任务是运行在run()方法中的。另外,
  需要注意的是:直接调用Runnable接口的run()方法不会创建新线程来执行任务,如果需要创建新线程执行任务,则需要调用Thread类的start()方法。start()方法public synchronized void start() {    //线程不是初始化状态,则直接抛出异常     if (threadStatus != 0)         throw new IllegalThreadStateException();     //添加当前启动的线程到线程组     group.add(this); 	//标记线程是否已经启动     boolean started = false;     try {         //调用本地方法启动线程         start0();         //将线程是否启动标记为true         started = true;     } finally {         try {             //线程未启动成功             if (!started) {                 //将线程在线程组里标记为启动失败                 group.threadStartFailed(this);             }         } catch (Throwable ignore) {             /* do nothing. If start0 threw a Throwable then                   it will be passed up the call stack */         }     } }  private native void start0();
  从start()方法的源代码,我们可以看出:
  start()方法使用synchronized关键字修饰,说明start()方法是同步的,它会在启动线程前检查线程的状态,如果不是初始化状态,则直接抛出异常。所以,一个线程只能启动一次,多次启动是会抛出异常的。
  这里,
  也是面试的一个坑:面试官:【问题一】能不能多次调用Thread类的start()方法来启动线程吗?【问题二】多次调用Thread线程的start()方法会发生什么?【问题三】为什么会抛出异常?
  调用start()方法后,新创建的线程就会处于就绪状态(如果没有分配到CPU执行),当有空闲的CPU时,这个线程就会被分配CPU来执行,此时线程的状态为运行状态,JVM会调用线程的run()方法执行任务。sleep()方法
  sleep()方法可以使当前线程休眠,其代码如下所示。//本地方法,真正让线程休眠的方法 public static native void sleep(long millis) throws InterruptedException;  public static void sleep(long millis, int nanos)     throws InterruptedException {     if (millis < 0) {         throw new IllegalArgumentException("timeout value is negative");     }      if (nanos < 0 || nanos > 999999) {         throw new IllegalArgumentException(             "nanosecond timeout value out of range");     }      if (nanos >= 500000 || (nanos != 0 && millis == 0)) {         millis++;     } 	//调用本地方法     sleep(millis); }
  sleep()方法会让当前线程休眠一定的时间,这个时间通常是毫秒值,这里需要注意的是:
  调用sleep()方法使线程休眠后,线程不会释放相应的锁。join()方法
  join()方法会一直等待线程超时或者终止,代码如下所示。public final synchronized void join(long millis)     throws InterruptedException {     long base = System.currentTimeMillis();     long now = 0;      if (millis < 0) {         throw new IllegalArgumentException("timeout value is negative");     }      if (millis == 0) {         while (isAlive()) {             wait(0);         }     } else {         while (isAlive()) {             long delay = millis - now;             if (delay <= 0) {                 break;             }             wait(delay);             now = System.currentTimeMillis() - base;         }     } }  public final synchronized void join(long millis, int nanos)     throws InterruptedException {      if (millis < 0) {         throw new IllegalArgumentException("timeout value is negative");     }      if (nanos < 0 || nanos > 999999) {         throw new IllegalArgumentException(             "nanosecond timeout value out of range");     }      if (nanos >= 500000 || (nanos != 0 && millis == 0)) {         millis++;     }      join(millis); }  public final void join() throws InterruptedException {     join(0); }
  join()方法的使用场景往往是启动线程执行任务的线程,调用执行线程的join()方法,等待执行线程执行任务,直到超时或者执行线程终止。interrupt()方法
  interrupt()方法是中断当前线程的方法,它通过设置线程的中断标志位来中断当前线程。此时,如果为线程设置了中断标志位,可能会抛出InteruptedExeption异常,同时,会清除当前线程的中断状态。这种方式中断线程比较安全,它能使正在执行的任务执行能够继续执行完毕,而不像stop()方法那样强制线程关闭。代码如下所示。public void interrupt() {     if (this != Thread.currentThread())         checkAccess();      synchronized (blockerLock) {         Interruptible b = blocker;         if (b != null) {             interrupt0();           // Just to set the interrupt flag             b.interrupt(this);             return;         }     }     //调用本地方法中断线程     interrupt0(); } private native void interrupt0(); 总结
  作为技术人员,要知其然,更要知其所以然,我那个朋友技术本身不错,各种框架拿来就用,基本没看过常用的框架源码和JDK中常用的API,属于那种CRUD型程序员,这次面试就栽在了一个简单的Thread类上,所以,大家在学会使用的时候,一定要了解下底层的实现才好啊!
  点击下方,第一时间了解华为云新鲜技术~
  华为云博客_大数据博客_AI博客_云计算博客_开发者中心-华为云
  #华为云开发者联盟#

惠州大亚湾区总工会组织新春活动为职工送祝福中工网讯据广东省总工会客户端消息,迎新春,广东省惠州市大亚湾区总工会启动了凝心聚力扬新帆年味送福工迎你活动,在大亚湾区总工会职工服务中心举办剪纸窗花贺新年职工亲子剪纸活动和年味插花23年可能不是华为崛起之年,而是生死之年据媒体报道,美国政府正在考虑切断美国供应商与中国华为公司之间的所有联系,禁止美国所有供应商向华为提供任何产品。报道称,美国一些官员向拜登政府提出了这一建议,若这项政策获得通过,所有8岁起在消防站长大,13年后他成长为消防员现代快报讯8岁那年,王青云跟随母亲第一次走进淮安市消防救援支队清江浦区城西消防救援站,那时他是消防队食堂阿姨李娟的儿子。消防员大哥哥们在训练之余,会带着小青云熟悉站里的一草一木,儿评论丨王飞时空相形诗思互证文质共胜时空相形诗思互证文质共胜评二十世纪英美小说的空间诗学研究文王飞二十世纪末,哥伦比亚大学出版社邀请20多位不同领域的著名学者,共同撰写了20世纪史,从战争民族主义帝国主义社会主义科学编辑说孩子,欢迎来到英语世界帮助孩子开启英语世界大门是否要让孩子从小学英语,总是让家长很矛盾。一方面怕从小给孩子太大的负担,让他们产生厌学心态但是另一方面也希望抓住孩子学英语的最佳契机,让他们在未来有能力与世界对话。视频加载中孩子,看兔展迎春接福,东莞文艺空间春节假期年味足人气旺兔年春节期间,东莞文联文艺空间里上演了一场精致可爱又内涵丰富的贺岁展吉庆年年陈湘波迎春作品展。整个春节期间文艺空间人气满满,艺术活跃了东莞的春节艺术气氛,让市民们的春假充满文化腔调45岁男子外出打工3年,深夜回家,发现异响疑遭贼报警,妻子我是一个正常女人男子结婚20年,在外打工三年,妻子在家独自经营一家水果店。这天深夜,男子回家,却撞见妻子和陌生男子共处一室。男子以为家里进了小偷,随即报了警。眼前的一幕让男子很气愤,女子却低声地说学习宣传贯彻党的二十大精神丨奋力开创田径事业发展新局面全面贯彻落实好党的二十大精神,我们要解放思想,改革创新,推进田径事业高质量发展,奋力开创新时代田径事业发展新局面,为2035年建成体育强国作出更大的贡献。对标建设体育强国的目标任务旗山时评攻坚奋进,再创小榄辉煌人勤春早,风气之先。大年初一,小榄镇永宁薄膜的生产车间内机器运转声不停走在拼经济最前线的广东制造业企业中,也有小榄身影。从春天出发,也必将抵达春天。大年初八,小榄新春第一会以攻坚奋狂飙陈书婷饰演者高叶,未来的影后热播剧狂飙己接近尾声,狂飙严格说来是一部男性剧,讲了兄弟情朋友义警匪纠葛,演员高叶饰演的陈书婷是其中的一个配角,但她塑造出来的大哥的女人大嫂形象却让人眼前一亮,直接盖过了女一的风头广州最东边的网红村又红了!2023年伊始,随着上千株樱花盛开,广州最东边的网红村增城区大埔围村再度成为焦点,一天吸引了上万名游客前往。实际上,从紫藤瀑布到醉蝶花,到樱花花海,大埔围花海一年四季,不同主体的开
如何看待德云社在如今相声界一家独大的情况?什么一家独大?纯属胡说!比他们强的多的是,他们不是为了传承艺术,只是为了钱而炒作。个人观点,谢邀!一花独放不是春,百花盛开春满园。谁人欲有独占心,相声乐坛便无醇。怎么这么提问题,德花菜的营养价值与功效是什么?花菜的营养价值与功效是什么?花菜的营养价值很高,有预防出血提高免疫力等功效。花菜又叫花椰菜菜花等,是营养价值很高的一种蔬菜,还有一定的药用价值。花菜中含有丰富的维生素K,可以预防内高洪波1比0胜法国队的那场比赛,对方到底有没有好好踢球?当时的情况是怎么样的?高洪波带队12强赛1平3负黯然辞职,但是没有高洪波,可能国足连12强赛的机会都没有。回头再看高洪波的国足,尽管他二度进宫以失败而告终,但在短暂的执教生涯里,仍然有一些比赛至今让我们如何快速解决羽毛球反手球打不远和扣杀无力的情况?首先针对您问题中的快速二字谈谈看法。羽毛球是一项对技术体能要求非常高的运动项目,没有教练的指导和常年的训练,基本上是无法成为一个羽坛高手的。当然,不排除一些选手拥有羽毛球天赋,那也每天晚上都做梦,而且睡眠很浅,稍有动静就醒了,是什么原因?作为医生,接触过很多这种类型的病人,不要小看睡眠质量不好,轻则面露疲惫之色,黑眼圈面色黑黄,让你越来越丑重则引起身体的其他疾病。希望能引起重视,给你几个建议,改观自己的睡眠质量。注晚上做什么事,有助于睡眠?傍晚可以适当做些运动,但不要太晚去做运动,这样反而适得其反。晚上有时间可以阅读一些书籍,有些人阅读过程中睡意就来了,阅读好的书籍也是一种享受。也可以听一些轻音乐,陶冶下情操,但不要产后该如何下奶?关于产后下奶的问题,主要包括两部分如何开奶及如何促进乳汁分泌。以下内容根据2016年哺乳指南进行解答。1。如何开奶?如果顺利分娩,母子健康状况良好,宝宝出生后应该尽早吸吮母乳,刺激地产已经越来越乏力了,未来什么行业会取代它?地产已经乏力,目前开两个行业,前景一定不错的,1。新能源车,估计2030年,燃油车停售,2050年,全面新能源车,目前,市场主流是,电池,氢能源,太阳能板,核能,目前,电动车与欧洲耳鸣很严重,听力下降,医生建议配助听器,助听器对耳鸣有好处吗?耳鸣很严重,听力下降,医生建议配助听器,助听器对耳鸣有好处吗?你好,有的助听器有耳鸣掩蔽的功能,但是效果也是引人而异的,有的人佩戴助听器后后可以改善耳鸣的现象,建议到专业正规的听力孩子想自杀又有自残行为是不是应该看心理医生?他不去家长咋办呀?孩子的问题很多是家长的问题。也许这句话你听起来不厚道,但我不得不这样回答你的问题,还望谅解。首先,孩子具体多大,这个我只能做个推测,孩子应该是在青春期阶段,尤其这种案例发生在初中学日产轩逸原车音响怎样,可以改装吗?日产轩逸的原车音响?这个音响之所以要加上冒号是因为日产轩逸的音响不能称之为音响,只能说是汽车喇叭虽然能够满足大部分人的需求,但是对音乐有要求的人群来说明显不够用。任何车的音响都是可