Java线程实现

线程把处理器的调度和资源分配分开,是cpu的最小调度单位。多个线程可以共享进程的内存资源,又可以独立调度。java线程关键方法都是通过高效的本地方法实现的。Java线程的主要实现方式有三种:内核实现、用户实现、内核用户混合实现。

1.内核实现

内核线程就是由内核调度、映射的线程。支持多线程的内核称为多线程内核。这种线程,所有操作都需要系统调度,需要在内核态和用户态切换,系统调用代价比较高。

2.用户实现

这种线程建立在用户空间,在用户态中建立、同步、销毁,不需要内核操作。这种操作非常快速且消耗低,可以支持更多的线程。但是线程阻塞和处理器的分配等功能在不借助内核态的情况下实现起来非常难,所以很少单独使用。

3.内核用户混合实现

最后是上面两种方式的结合,通过用户线程实现线程的创建、切换、析构等,而通过内核提供的轻量级进程实现线程的调度及处理器的映射。

JDK采用的是第二种方式即内核实现,每一个java线程都会对应内核提供的一条轻量级进程。

Java线程调度

线程调度就是分配处理器使用权的过程,主流的调度方式有两种:协同式线程调度和抢占式线程调度。
协同式线程调度中线程的执行时间由线程自身控制,线程执行完后,要主动通知系统切换线程。这种方式实现起来比较简单,且不存在线程同步问题。但是由于线程自身控制切换操作,若某个线程出现问题,可能会导致系统的崩溃。
抢占式线程调度中线程的调度由系统控制,这样就可以避免某个线程挂掉而导致整个系统崩溃。我们的jdk线程就是采用的这种调度方式,系统运行起来会更加的稳定。
当然我们可以通过设置线程的优先级来提高某些线程的执行几率,但是这种方式存在很大的不确定性。因为线程优先级的实现依赖于具体的操作系统平台,不同的平台优先级实现不同,可能会导致java中不同线程优先级在一些平台上却是按相同优先级进行调度的,另外操作系统还可能根据某些策略来忽略线程优先级,所以线程在cpu中的具体调度策略和执行顺序是不可知的,我们不能想当然的臆测线程的执行逻辑。

java线程生命周期

Java线程主要存在5中状态:新建(new)、运行(runnable)、无限期等待(waitting)、有限期等待(timed waiting)、阻塞(blocked)、结束(terminated)。
1.新建:创建后尚未启动的线程。
2.运行:正在执行及等待cpu时间片的线程。
3.无限期等待:不会被分配时间片,等待唤醒的线程。主要包括:使用了Object.wait()、Thread.join()、LockSupport.park()等无timeout参数方法的线程。
4.有期限等待:这种状态的线程也不会被分配时间片,但是在一定时间后系统会自动唤醒它们。主要包括:使用了Thread.sleep()及上面3中几个带timeout参数方法的线程。
5.结束:已经终止的线程。

线程池的优点

由于java线程是通过内核中的轻量级进程实现的,线程创建和销毁都需要切换到内核态,线程生命周期开销非常高。同时新建线程也会导致请求延迟一会才能被处理。另外由于每个线程都会分配一些独立的内存空间,若创建过多的线程会增加内存的占用,同时大量空闲的线程持有对象强引用,会给垃圾回收带来很大的压力,大量的线程竞争cpu资源也会产生很大的性能开销,降低程序的执行速度。在后端服务中经常会出现某些rpc接口的延迟抖动会导致整个服务所有接口性能下降,主要就是因为:依赖的外部接口抖动延迟响应时间变长,请求接口的线程阻塞同时大量请求重试,这时大量新线程被创建,cpu频繁进行用户态内核切换及大量线程争用cpu,导致服务性能逐步下降。线程池的出现非常好的解决了上面的问题,现在代码中已经很少能见到直接new Thread的操作了,有这种操作的程序猿要么是扫地圣僧,要么就是删库跑路的狠人了,哈哈哈哈。

线程及线程池使用注意点

1.尽量避免使用守护线程

Jvm在正常关闭时,会先并行执行关闭钩子及所有已提交和执行中的普通线程,然后去处理定义了finalize方法的对象,做好这些后就会直接结束运行,不会管是否有是正在执行的守护线程,若我们在自定义的守护线程中进行了业务操作或IO操作之类的,就可能造成意外的业务错误。

2.避免改变线程优先级

jvm中的线程优先级只能作为线程调度的参考,线程并不一定按优先级高低顺序执行,这是因为jvm中线程优先级是通过映射系统调度优先级实现的,依赖于特定的平台,而不同平台实现的调度优先级不同,因此两个不同优先级的线程可能被映射成相同的调度优先级。除此之外使用优先级还可能会导致某些线程一直无法获取cpu的调度,进而导致线程的饥饿问题。

3.依赖性任务可能导致线程的饥饿死锁

在线程池中,如果任务依赖于其他任务,并且依赖的任务也在同一线程池中执行,那么便可能产生死锁。当依赖的任务被拒绝或者一直停留在工作队列中,那么任务就会一直阻塞并一直占用线程,队列中任务也获取不到这个线程,就会产生死锁,这种死锁被称为线程饥饿死锁。

4.线程池中的任务应该是同类型的独立任务

计算密集型任务一定不能和IO密集型共用同一个线程池。道理其实很简单,我们举个例子:我们有两个线程并行执行,其中一个需要9毫秒,而另一个需要1毫秒,当我们采用串行执行时任务执行所需时间为10毫秒,而当我并行执行时任务执行所需时间为9毫秒,线程的切换可能还需要一些时间(假设2毫秒),这样算下来抛除线程切换造成的cpu资源浪费,结果并行时间反而还没有串行快,吃力不讨好啊。实际上计算密集型和IO密集型任务不但应该使用不同的线程池,连线程池大小的配置策略也是大不相同,小伙伴们要注意下。由此我们可以进一步推出:执行时间较长的任务不能和执行时较短的任务共用一个线程池,执行时间较长的任务不仅可能造成线程阻塞,也会增加执行时间较短任务的响应时间,甚至当长时间任务的qps大于线程池中的线程数量时,可能会出现所有线程都在执行长时间任务的现象,严重影响服务的性能。总而言之,线程池中的任务应该是同类型的独立任务,并且我们需要根据任务类型去合理配置线程池的线程数量。

java线程实现及线程池的使用相关推荐

  1. Java中四种线程池介绍

    个人资源与分享网站:http://xiaocaoshare.com/ Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而是一个执行线程的工具.真正的线 ...

  2. Java多线程设计模式(4)线程池模式

    前序: Thread-Per-Message Pattern,是一种对于每个命令或请求,都分配一个线程,由这个线程执行工作.它将"委托消息的一端"和"执行消息的一端&qu ...

  3. java多线程抽奖_java 线程池、多线程并发实战(生产者消费者模型 1 vs 10) 附案例源码...

    导读 前二天写了一篇<Java 多线程并发编程>点我直达,放国庆,在家闲着没事,继续写剩下的东西,开干! 线程池 为什么要使用线程池 例如web服务器.数据库服务器.文件服务器或邮件服务器 ...

  4. Java并发编程:线程池

    一.为什么使用线程池 使用线程的时候直接就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降 ...

  5. Java自带的线程池Executors.newFixedThreadPool

    线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理.当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程 ...

  6. 线程池 java 新建方式_Java线程池的四种创建方式

    Java通过Executors提供四种线程池,分别为: newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程. newFi ...

  7. 掌握JAVA多线程的利器-线程池

    为什么80%的码农都做不了架构师?>>>    相信大多数接触过多线程的朋友都会有这样的困惑,明明使用了多线程,为何还是一团糟?用下面两幅图再合适不过了: 理想情况下的多线程VS 现 ...

  8. 【Java 并发编程】线程池机制 ( 线程池状态分析 | 线程池状态转换 | RUNNING | SHUTDOWN | STOP | TIDYING | TERMINATED )

    文章目录 一.线程池状态分析 一.线程池状态分析 线程池的状态在 ThreadPoolExecutor 源码中定义 : private final AtomicInteger ctl = new At ...

  9. 【Java 并发编程】线程池机制 ( 线程池执行任务细节分析 | 线程池执行 execute 源码分析 | 先创建核心线程 | 再放入阻塞队列 | 最后创建非核心线程 )

    文章目录 一.线程池执行任务细节分析 二.线程池执行 execute 源码分析 一.线程池执行任务细节分析 线程池执行细节分析 : 核心线程数 101010 , 最大小成熟 202020 , 非核心线 ...

  10. 【Java 并发编程】线程池机制 ( 线程池阻塞队列 | 线程池拒绝策略 | 使用 ThreadPoolExecutor 自定义线程池参数 )

    文章目录 一.线程池阻塞队列 二.拒绝策略 三.使用 ThreadPoolExecutor 自定义线程池参数 一.线程池阻塞队列 线程池阻塞队列是线程池创建的第 555 个参数 : BlockingQ ...

最新文章

  1. 使用python moviepy提取视频中的音频,同时对音频数据进行数据可视化分析
  2. 菜鸟学C语言(五)之求定积分
  3. 【归并排序】休息(jzoj 3462)
  4. 深度学习pytorch--softmax回归(一)
  5. java 垂直走马灯多行,Android实现图文垂直跑马灯效果
  6. 阿里云爬虫风险管理产品商业化,为云端流量保驾护航
  7. JSP 实现登录注册功能
  8. 金融反欺诈-交易基础介绍
  9. DolphinScheduler 调度系统
  10. 思科交换机配置trunk模式及vtp
  11. ACM/ICPC 2018亚洲区预选赛北京赛站网络赛-B:Tomb Raider(模拟+二进制枚举子串)
  12. 深度学习目标检测---使用labelimg对自己的数据集进行标记(windows系统)
  13. ROMS四维变分测试
  14. 西安电子科技大学计算机网络技术,计算机网络技术与应用课后题答案(西安电子科技大学).doc...
  15. 鸿蒙系统电脑适配双面打印机,win10系统实现打印机双面打印的操作方法
  16. 【GitHub或GitLab rejected】error: failed to push some refs to,Updates were rejected...
  17. 如何区分VR、AR和MR
  18. 世界十大经典汽车赛道盘点
  19. Linux下多个进程可以同时打开同一个文件吗?文件描述符与打开文件的关系?
  20. Load balancer does not have available server for client: wr-fac

热门文章

  1. 如何调整反光镜和座椅的位置 为您支招
  2. java 并发包之 LongAdder 源码分析
  3. pom.xml 配置之:snapshot 快照库和 release发布库 的区别
  4. Linux rm命令、Linux touch命令、Linux tee命令
  5. SOA (面向服务的架构)
  6. ACM-ICPC 2018 徐州赛区网络预赛 I. query 树状数组
  7. BZOJ 1567: [JSOI2008]Blue Mary的战役地图
  8. 抓取各个浏览器引擎关键字,,百度学术关键字
  9. 拦截器及 Spring MVC 整合
  10. js 当前日期增加自然月