一文带你深入剖析Java线程池的前世今生

目录
  • 由线程到线程池
    • 线程在做什么
    • 为什么需要线程池
  • 线程池实现原理
  • 总结

由线程到线程池

线程在做什么

灵魂拷问:写了那么多代码,你能够用一句话简练描述线程在干啥吗?

public class Demo01 {

  public static void main(String[] args) {
    var thread = new Thread(() -> {
      System.out.println("Hello world from a Java thread");
    });
    thread.start();
  }
}

我们上面的这个用线程输出字符串的代码来进行说明。我们知道上面的Java代码启动了一个线程,然后执行lambda函数,在以前没有lambda表达式的时候我们可以使用匿名内部类实现,向下面这样。

public class Demo01 {

  public static void main(String[] args) {
    var thread = new Thread(new Runnable() {
      @Override
      public void run() {
        System.out.println("Hello world from a Java thread");
      }
    });
    thread.start();
  }
}

但是本质上Java编译器在编译的时候都认为传递给他的是一个对象,然后执行对象的run方法。刚刚我们使用的Thread的构造函数如下:

    public Thread(Runnable target) {
        this(null, target, "Thread-" + nextThreadNum(), 0);
    }

Thread在拿到这个对象的时候,当我们执行Threadstart方法的时候,会执行到一个native方法start0

当JVM执行到这个方法的时候会调用操作系统给上层提供的API创建一个线程,然后这个线程会去解释执行我们之前给Thread对象传入的对象的run方法字节码,当run方法字节码执行完成之后,这个线程就会退出。

看到这里我们仔细思考一下线程在做一件什么样的事情,JVM给我们创建一个线程好像执行完一个函数(run)的字节码之后就退出了,线程的生命周期就结束了。确实是这样的,JVM给我们提供的线程就是去完成一个函数,然后退出(记住这一点,这一点很重要,为你后面理解线程池的原理有很大的帮助)。事实上JVM在使用操作系统给他提供的线程的时候也是给这个线程传递一个函数地址,然后让这个线程执行完这个函数。只不过JVM给操作系统传递的函数,这个函数的功能就是去解释执行字节码,当解释执行字节码完成之后,这个函数也会退出(被系统回收)。

看到这里可以将线程的功能总结成一句话:执行一个函数,当这个函数执行完成之后,线程就会退出,然后被回收,当然这个函数可以调用其他的函数,可能你会觉得这句话非常简单,但是这句话会我们理解线程池的原理非常有帮助。

为什么需要线程池

上面我们已经谈到了,当我们执行start的方法的时候,最终会走到start0方法,这是一个native方法,JVM在执行这个方法的时候会通过系统底层函数创建一个线程,然后去执行run方法,这里需要注意,创建线程是需要系统资源的,比如说内存,因为操作系统是系统资源的管理者,因此一般需要系统资源的方法都需要操作系统的参与,因此创建线程需要操作系统的帮忙,而一旦需要操作系统介入,执行代码的状态就需要从用户态到内核态转换(内核态能够执行许多用户态不能够执行的指令),当操作系统创建完线程之后有需要返回用户态,我们的代码将继续被执行,整个过程像下面这样。

从上图可以看到我们需要两次的上下文切换,同时还需要执行一些操作系统的函数,这个过程是非常耗时间的,如果在并发非常高的情况,我们频繁的去生成线程然后销毁,这对我们程序的性能影响还是非常大的。因此许许多多聪明的程序员就想能不能不去频繁的创建线程而且也能够完成我们的功能——我们创建线程的目的就是想让我们的程序完成的更加快速,让多个不同的线程同时执行不同的任务。于是线程池就被创造出来了。线程池的结构大致如下所示:

线程池实现原理

在前面我们已经提到了关于线程池和线程比较重要的两个点:

  • 线程就是执行一个函数。
  • 线程池当中的线程可以执行很多函数,但是不会退出。

凭借你朴素的情感,你觉得如何实现上面两个要求。答案就是在一个函数当中进行while循环,然后不断的从任务队列当中获取任务函数,然后进行执行,直到要求停止线程池当中的线程的时候线程再进行退出,整个过程的代码大致如下所示:

  public void run() {
    while (!isStopped) {
      try {
        Runnable task = tasks.take();
        task.run();
      } catch (InterruptedException e) {
        // do nothing
      }
    }
  }

关于线程池要有一部分细节也很重要,比如说我们需要一个并发安全的阻塞队列,如何保证所有线程正常退出等等,我们在下篇文章当中进行实现,而且将仔细分析这里面的细节。

总结

在本篇文章当中主要给大家介绍了线程到线程池的演化过程,主要介绍线程池实现的基本原理,主要解读了线程池背后的基本原理,希望大家有所收获!

以上就是一文带你深入剖析Java线程池的前世今生的详细内容,更多关于Java线程池的资料请关注我们其它相关文章!

(0)

相关推荐

  • java 线程池存在的意义

    目录 前言 创建线程 继承Thread 实现Runnable接口 实现Callable接口 线程池 小结 前言 再次之前我已经花费大量篇幅介绍了Java原声锁和Lock锁.在文章中提到偏向送.轻量级锁.重量级锁.公平锁.非公平锁.自旋锁.自适应自旋锁.分布式锁.分段锁等等锁.所有的锁都是为了解决一个问题应运而生的那就是并发.而产生并发的原因是CPU的发展导致我们程序多线程运行.在代码中我们也经常通过多线程来提高产品的吞吐量. 在锁章节中我们也是通过多线程案例模拟锁的产生的.那么Java领域中有哪

  • 简单聊一聊Java线程池ThreadPoolExecutor

    目录 简介 参数说明 如何创建线程池 拒绝策略 总结 简介 ThreadPoolExecutor是一个实现ExecutorService接口的线程池,ExecutorService是主要用来处理多线程任务的一个接口,通常比较简单是用法是由Executors工厂类去创建. 线程池主要解决了两个不同的问题: 在执行大量异步任务时,为了能够提高性能,通常会减少每个任务的调用开销. 提供了一系列多线程任务的管理方法,便于多任务执行时合理分配资源以及一些异常情况的处理.每个ThreadPoolExecut

  • Java线程池必知必会知识点总结

    目录 1.线程数使用开发规约 2.ThreadPoolExecutor源码 1.构造函数 2.核心参数 3.execute()方法 3.线程池的工作流程 4.Executors创建返回ThreadPoolExecutor对象(不推荐) 1.Executors#newCachedThreadPool=>创建可缓存的线程池 2.Executors#newSingleThreadExecutor=>创建单线程的线程池 3.Executors#newFixedThreadPool=>创建固定长度

  • Java线程池实现原理总结

    目录 一.线程池参数 二.线程池执行流程 三.四种现成的线程池 要理解实现原理,必须把线程池的几个参数彻底搞懂,不要死记硬背 一.线程池参数 1.corePoolSize(必填):核心线程数. 2.maximumPoolSize(必填):最大线程数. 3.keepAliveTime(必填):线程空闲时长.如果超过该时长,非核心线程就会被回收. 4.unit(必填):指定keepAliveTime的时间单位.常用的有:TimeUnit.MILLISECONDS(毫秒).TimeUnit.SECON

  • 一文带你深入剖析Java线程池的前世今生

    目录 由线程到线程池 线程在做什么 为什么需要线程池 线程池实现原理 总结 由线程到线程池 线程在做什么 灵魂拷问:写了那么多代码,你能够用一句话简练描述线程在干啥吗? public class Demo01 {   public static void main(String[] args) {     var thread = new Thread(() -> {       System.out.println("Hello world from a Java thread"

  • 一篇文章带你搞懂Java线程池实现原理

    目录 1. 为什么要使用线程池 2. 线程池的使用 3. 线程池核心参数 4. 线程池工作原理 5. 线程池源码剖析 5.1 线程池的属性 5.2 线程池状态 5.3 execute源码 5.4 worker源码 5.5 runWorker源码 1. 为什么要使用线程池 使用线程池通常由以下两个原因: 频繁创建销毁线程需要消耗系统资源,使用线程池可以复用线程. 使用线程池可以更容易管理线程,线程池可以动态管理线程个数.具有阻塞队列.定时周期执行任务.环境隔离等. 2. 线程池的使用 /** *

  • 一篇文章带你深入了解Java线程池

    目录 线程池模型 常用线程池 ThreadPoolExecutor 构造函数参数说明 线程池默认工作行为 ForkJoinPool FutureTask 线程数量分析 CPU密集型 IO密集型 总结 线程池模型 一般的池化模型会有两个方法,用于获取资源和释放资源,就像这样: public interface XXPool{ XX acquire(); void release(); } 但是,工程中的线程池一般是生产者和消费者模型,线程池是消费者,任务的提交者是生产者,下面是一个简化的线程池模型

  • 一文带你弄懂Java中线程池的原理

    目录 为什么要用线程池 线程池的原理 ThreadPoolExecutor提供的构造方法 ThreadPoolExecutor的策略 线程池主要的任务处理流程 ThreadPoolExecutor如何做到线程复用的 四种常见的线程池 newCachedThreadPool newFixedThreadPool newSingleThreadExecutor newScheduledThreadPool 小结 在工作中,我们经常使用线程池,但是你真的了解线程池的原理吗?同时,线程池工作原理和底层实

  • 一文了解Java 线程池的正确使用姿势

    目录 概述 线程池介绍 线程池创建 ThreadPoolExecutor创建 Executors创建 newFixedThreadPool newCachedThreadPool newSingleThreadExecutor newScheduledThreadPool newWorkStealingPool 线程池关键API和例子 提交执行任务API 关闭线程池API 线程池监控API 扩展API 使用注意事项 概述 线程池在平时的工作中出场率非常高,基本大家多多少少都要了解过,可能不是很全

  • 一文带你深入了解Java中延时任务的实现

    目录 概述 JAVA DelayQueue DelayQueue的实现原理 DelayQueue实现延时队列的优缺点 时间轮算法 时间轮的具体实现 进阶优化版时间轮算法 时间轮算法的应用 小结 redis延时队列 mq延时队列 rocketmq延时消息 rocketmq的精准延时消息 总结 概述 延时任务相信大家都不陌生,在现实的业务中应用场景可以说是比比皆是.例如订单下单15分钟未支付直接取消,外卖超时自动赔付等等.这些情况下,我们该怎么设计我们的服务的实现呢? 笨一点的方法自然是定时任务去数

  • 一文带你全面了解Java Hashtable

    目录 概述 介绍和使用 核心机制 实现机制 扩容机制 源码解析 成员变量 构造函数 put方法 get方法 remove方法 总结 概述 HashTable是jdk 1.0中引入的产物,基本上现在很少使用了,但是会在面试中经常被问到,你都知道吗: HashTable底层的实现机制是什么? HashTable的扩容机制是什么? HashTable和HashMap的区别是什么? 介绍和使用 和HashMap一样,Hashtable也是一个散列表,它存储的内容是键值对(key-value)映射, 重要

  • 一文带你深入了解Java TreeMap

    目录 概述 TreeMap介绍 构造方法 关键方法 使用案例 核心机制 实现原理 源码解析 成员变量 查找get方法 插入put方法 删除remove方法 概述 TreeMap是Map家族中的一员,也是用来存放key-value键值对的.平时在工作中使用的可能并不多,它最大的特点是遍历时是有顺序的,根据key的排序规则来,那么它具体是如何使用,又是怎么实现的呢?本文基于jdk8做一个讲解. TreeMap介绍 TreeMap是一个基于key有序的key value散列表. map根据其键的自然顺

  • 一文带你搞懂Java中Synchronized和Lock的原理与使用

    目录 1.Synchronized与Lock对比 2.Synchronized与Lock原理 2.1 Synchronized原理 2.2 Lock原理 3.Synchronized与Lock使用 Synchronized Lock 4.相关问题 1.Synchronized与Lock对比 实现方式:Synchronized是Java语言内置的关键字,而Lock是一个Java接口. 锁的获取和释放:Synchronized是隐式获取和释放锁,由Java虚拟机自动完成:而Lock需要显式地调用lo

  • 一文带你彻底理解Java序列化和反序列化

    Java序列化是什么? Java序列化是指把Java对象转换为字节序列的过程,Java反序列化是指把字节序列恢复为Java对象的过程. 反序列化: 客户端重文件,或者网络中获取到文件以后,在内存中重构对象. 序列化: 对象序列化的最重要的作用是传递和保存对象的时候,保证对象的完整性和可传递性.方便字节可以在网络上传输以及保存在本地文件. 为什么需要序列化和反序列化 实现分布式 核心在于RMI,可以利用对象序列化运行远程主机上的服务,实现运行的时候,就像在本地上运行Java对象一样. 实现递归保存

随机推荐