博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java线程生命周期之旅
阅读量:2166 次
发布时间:2019-05-01

本文共 3740 字,大约阅读时间需要 12 分钟。

java线程生命周期

  • 关于线程生命周期的不同状态,java5以后,线程状态已内部枚举类的方式明确定义在java.lang.Thread.State
  • 1.新建(new),表示该线程刚被创建还未真正启动,可认为其是java内部状态
  • 2.就绪(RUNNABLE),表示该线程已经在JVM中执行,由于执行需要资源,他可能处于正运行,也可能是等待系统分配给它CPU片段,在就绪队列里面
  • 3.在其他一些分析中,会有额外区分一种状态RUNNING,但从JAVA API(内部线程状态枚举类)来说,并不能变现处理
  • 4.阻塞(BLOCKED),这个状态和同步相关,表示线程在等待获取Monitor lock来锁住资源供自己使用。如线程通过synchronized区获取某个锁,但是其他线程已经独占,那么该线程就处于阻塞。
  • 5.等待(Waiting),表示正在等待期限线程采取某些操作。场景如生产者消费者模式,发现任务条件不满足,就让消费者线程等待(Wait),然后生产者线程准备数据,输出结果,然后通过notify等动作,通知消费者可以继续工作。Thread.join()也会令线程进入WAITING.
  • 6.计时等待(TIMED_WAIT),其进入条件和等待状态类似,但是调用的是存在超时条件的方法,如wait或join等方法的指定超时版本。如下
    public final native void wait(long timeout) throws InterruptedException;
  • 7.终止(TERMINATED),不管意外退出还是正常执行结束,线程已经完成。也叫死亡。

若线程被调用两次start()方法

  • java线程不允许启动两次,第二次比如抛出运行时IllegalThreadStateException,多次调用会被认为编译错误
  • 第二次调用start()方法时,线程可能处于终止或其他(非new)状态。不论如何,都不可以再次被启动

知识扩展

线程

  • 从OS角度,线程是系统调度最小单元,一个进程包含多个线程
  • 作为任务真正运作者,有自己的栈(Stack)、寄存器(Register)、本地存储(Thread Local)等,但是回合进程内其他线程共享FileDescriptor、虚拟地址空间等
  • 具体实现,分为内核线程、用户线程,java线程实现与JVM相关。如最熟悉的Sun/Oracle JDK,其线程经历演进,java1.2后,jdk抛弃所谓Green Thread(用户调度的线程),现在的模型是一对一映射到OS内核线程

    Thread源码

  • 基本操作逻辑以JNI(java native interface)调用的本地代码
    Runnable task = () -> {System.out.println("Hello World!");};Thread myThread = new Thread(task);myThread.start();myThread.join();
  • 上述实现利弊
    优点:得益于java精细粒度的线程和相关并发操作,胜任构建高扩展大型应用。缺点:其复杂性也提高了并发编程的门槛。
  • 线程基本操作
    1.创建、启动线程,执行join方法等待结束,Runnable不会受java不支持类多继承的限制,重用代码实现,提高代码复用。Runnable task = () -> {System.out.println("Hello World!");};Thread myThread = new Thread(task);myThread.start();myThread.join();2.将上述逻辑start()和join()改成如下结构,提交task线程任务,线程创建、管理交由线程池,也能利用Future等机制更好处理执行结果。Future future = Executors.newFixedThreadPool(1).submit(task).get();

影响线程状态因素

  • 线程自身方法,除start(),还有多个join方法,等待线程结束;yield是告知调度器,主动让出CPU;另外,就是一些标记过时的resume、stop、suspend等,为了destroy、stop会被移除
  • 基于Object提供的基础方法(wait/notify/notifyAll).如果我们持有某个对象的Monitor锁,调用wait会让当前线程处于WAITTING状态,直到其他线程notify或者notifyAll,本质上是提供Monitor的获取和释放的能力,是基本线程间通信方式
  • 并发类库的工具,如CountDownLatch.await()会让当前线程进入等待状态,指定latch被基数为0,这可以看做线程间通信的Signal
  • 线程状态转换与方法直接的对应图
    java线程状态与方法对应关系图

API使用

  • 守护线程(Daemon Thread),有时应用需要一个长期驻留的服务程序,不希望其影响应用退出,就可以将其设置为守护线程,如果jvm发现只有守护线程存在,将结束进程。
    注意:必须在线程启动前设置Thread daemonThread = new Thread();daemonThread.setDaemon(true);daemonThread.start();
  • Spurious wakeup ,多核CPU系统中,线程等待存在一种肯,即在无任何线程广播或发出信号情况下,线程被唤醒,如处理不当就会出现诡异并发问题,所以在在等待条件过程中,建议采用如下模式来写
    // 推荐while ( isCondition()) {waitForAConfition(...);}// 不推荐,可能引入bugif ( isCondition()) {waitForAConfition(...);}
  • Thread.opSpinWait,java9 中引入特性,用于自旋锁,自旋锁(spin-wait,busy-waitng,未获得锁,一直处于空转),也可以认为岂不是一种锁,而是一个针对短期等待的性能优化技术。“onSpinWait()”无任何行为保证,而是对jvm的暗示,jvm可能会利用CPU的pause指令进一步提高性能,性能敏感应用可以关注。
  • 慎用ThreadLLocal(本地存储),java提高保存线程私有的机制,在整个线程生命周期有效,方便地在一个线程关联的不同业务模块之间传递信息,如事务id,Cookie等上下文相关信息。其实现结构如下,数据存储于ThreadLocalMap,内部为弱引用
    1.当key为null时,该条目为”废弃条目”,相关”value”的回收,往往依赖于几个关键点,即set,remove,rehashstatic class ThreadLocalMap {  static class Entry extends WeakReference
    > { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal
    k, Object v) { super(k); value = v; } } // …}2.set示例,具体逻辑在cleanSomeSlots 和 expungeStaleEntry 之中private void set(ThreadLocal
    key, Object value) { Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); for (Entry e = tab[i];; …) { //… if (k == null) {// 替换废弃条目 replaceStaleEntry(key, value, i); return; } } tab[i] = new Entry(key, value); int sz = ++size;// 扫描并清理发现的废弃条目,并检查容量是否超限 if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash();// 清理废弃条目,如果仍然超限,则扩容(加倍)}
  • 结合java引用类型,发现一个特别地方,通常弱引用都会和引用队列配合清理机制,但是ThreadLocal(本地存储),是个例外。这意味废弃项目回收依赖于显式地触发,否则就要等线程结束,进而回收相应的ThreadLocalMap.这也是很多OOM来源。因此谨慎使用ThreadLocal,在使用后一定要自己负责remove,并且不要和线程池配合,因为worker线程往往不会退出,仍然会造成OOM.

转载地址:http://yhjzb.baihongyu.com/

你可能感兴趣的文章
Leetcode C++《热题 Hot 100-17》461.汉明距离
查看>>
Leetcode C++《热题 Hot 100-18》538.把二叉搜索树转换为累加树
查看>>
Leetcode C++《热题 Hot 100-19》543.二叉树的直径
查看>>
Leetcode C++《热题 Hot 100-21》581.最短无序连续子数组
查看>>
Leetcode C++《热题 Hot 100-22》2.两数相加
查看>>
Leetcode C++《热题 Hot 100-23》3.无重复字符的最长子串
查看>>
Leetcode C++《热题 Hot 100-24》5.最长回文子串
查看>>
Leetcode C++《热题 Hot 100-26》15.三数之和
查看>>
Leetcode C++《热题 Hot 100-28》19.删除链表的倒数第N个节点
查看>>
Leetcode C++《热题 Hot 100-29》22.括号生成
查看>>
Leetcode C++《热题 Hot 100-44》102.二叉树的层次遍历
查看>>
Leetcode C++《热题 Hot 100-47》236.二叉树的最近公共祖先
查看>>
Leetcode C++《热题 Hot 100-48》406.根据身高重建队列
查看>>
《kubernetes权威指南·第四版》第二章:kubernetes安装配置指南
查看>>
Leetcode C++《热题 Hot 100-49》399.除法求值
查看>>
Leetcode C++《热题 Hot 100-51》152. 乘积最大子序列
查看>>
Leetcode C++ 《第181场周赛-1》 5364. 按既定顺序创建目标数组
查看>>
Leetcode C++ 《第181场周赛-2》 1390. 四因数
查看>>
阿里云《云原生》公开课笔记 第一章 云原生启蒙
查看>>
阿里云《云原生》公开课笔记 第二章 容器基本概念
查看>>