线程池的特点:

降低资源:通过重复利用已创建的线程降低线程创建和销毁的损耗

提高效率:当任务到底时,不需要等待,立即执行

方便管理:统一分配,调优和监控等

线程池的创建方式:

1.CachedThreadPool:创建一个可缓存线程池,灵活回收空闲线程

public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int temp = i;
newCachedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "->" + temp);
}
});
}
}
}

打印后可以发现:同一个线程有被再次利用,线程池理论大小是无限的

2.FixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待

        ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(5);

其他代码不变,打印发现五个线程都在复用

3.ScheduledThreadPool:支持定时性地执行任务

public class ThreadPoolDemo {
public static void main(String[] args) {
ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 10; i++) {
final int temp = i;
newScheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "->" + temp);
}
}, 3, TimeUnit.SECONDS);
}
}
}

观察打印:等待3s后打印

4.SingleThreadExecutor:单线程

        ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();

打印发现只调用了一个线程

线程池的原理:

四种方式都走了ThreadPoolExecutor的构造方法:

    public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
    public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}

corePoolSize:最大核心线程数:实际运用线程数

maximumPoolSize:最大线程数:线程池最多创建线程数

如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会创建一个线程去执行这个任务

如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务;

如果队列已经满了,则在总线程数不大于maximumPoolSize的前提下,则创建新的线程;

如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策略进行处理;

如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。

写一段代码来理解:

public class MyThreadPool {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
//核心线程数为1
1,
//最大线程数为2
2,
//保持存活时间0毫秒,意思是用完不回收
0L, TimeUnit.MILLISECONDS,
//阻塞队列
new LinkedBlockingQueue<Runnable>(3));
threadPoolExecutor.execute(new TaskThread("task1"));
threadPoolExecutor.execute(new TaskThread("task2"));
threadPoolExecutor.execute(new TaskThread("task3"));
}
} class TaskThread implements Runnable {
private String threadName; TaskThread(String threadName) {
this.threadName = threadName;
} @Override
public void run() {
System.out.println(threadName);
}
}

提交task1时候,创建一个线程直接执行,此时核心线程数等于存在线程数

提交task2时候,存放在队列缓存,此时存在线程数等于最大线程数

提交task3时候,也存放在队列中缓存,而我阻塞队列大小是3,现在只存放了两个,所以不会报错

理论上,提交到task5都不会报错,最大线程数2+队列大小3=5;但是提交task6一定会报错

另外一点:如果提交到task4,打印线程名称:发现只调用了线程1,第2个线程没有调用过

因为:task1提交时候,task2,task3,task4存放在队列中,恰好到达队列最大值,所以不创建新线程,而是线程1依次执行这三个任务

合理配置线程池:

原则:

CPU密集:任务需要大量的运算,但没有阻塞的情况,CPU可以全速运行

IO密集:任务需要大量的IO操作,产生阻塞

CPU密集型线程数越少越好,最好配置为CPU核心数量

IO密集型线程数要尽量多,最好配置为CPU核心数量*2

Java深入学习(3):线程池原理的更多相关文章

  1. java多线程系列(六)---线程池原理及其使用

    线程池 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 java多线程系列(三)之等待通知 ...

  2. Java多线程学习之线程池源码详解

    0.使用线程池的必要性 在生产环境中,如果为每个任务分配一个线程,会造成许多问题: 线程生命周期的开销非常高.线程的创建和销毁都要付出代价.比如,线程的创建需要时间,延迟处理请求.如果请求的到达率非常 ...

  3. JAVA多线程学习七-线程池

    为什么用线程池 1.创建/销毁线程伴随着系统开销,过于频繁的创建/销毁线程,会很大程度上影响处理效率 例如: 记创建线程消耗时间T1,执行任务消耗时间T2,销毁线程消耗时间T3 如果T1+T3> ...

  4. 《java学习三》并发编程 -------线程池原理剖析

    阻塞队列与非阻塞队 阻塞队列与普通队列的区别在于,当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞.试图从空的阻塞队列中获取元素的线程将会被阻塞,直到 ...

  5. Java 线程池原理分析

    1.简介 线程池可以简单看做是一组线程的集合,通过使用线程池,我们可以方便的复用线程,避免了频繁创建和销毁线程所带来的开销.在应用上,线程池可应用在后端相关服务中.比如 Web 服务器,数据库服务器等 ...

  6. Java 并发编程——Executor框架和线程池原理

    Eexecutor作为灵活且强大的异步执行框架,其支持多种不同类型的任务执行策略,提供了一种标准的方法将任务的提交过程和执行过程解耦开发,基于生产者-消费者模式,其提交任务的线程相当于生产者,执行任务 ...

  7. Java 并发编程——Executor框架和线程池原理

    Java 并发编程系列文章 Java 并发基础——线程安全性 Java 并发编程——Callable+Future+FutureTask java 并发编程——Thread 源码重新学习 java并发 ...

  8. Java线程池原理解读

    引言 引用自<阿里巴巴JAVA开发手册> [强制]线程资源必须通过线程池提供,不允许在应用中自行显式创建线程. 说明:使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销 ...

  9. 手写一个线程池,带你学习ThreadPoolExecutor线程池实现原理

    摘要:从手写线程池开始,逐步的分析这些代码在Java的线程池中是如何实现的. 本文分享自华为云社区<手写线程池,对照学习ThreadPoolExecutor线程池实现原理!>,作者:小傅哥 ...

  10. java多线程系类:JUC线程池:03之线程池原理(二)(转)

    概要 在前面一章"Java多线程系列--"JUC线程池"02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包 ...

随机推荐

  1. btcWallet系列之一-grpc模块

    btcwallet对外服务 btcwallet除了像btcd对外提供rpc服务以外,还提供了grpc服务,同时grpc采用的是protobuf来实现. 这方便与不同语言进行交互,降低客户端代码编写量. ...

  2. python--线程知识详解

    Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元. 1.1.threading模块 threading模块建立在_thread模块之上.thread模块以低级=原始的方式来处理 ...

  3. Nginx on Docker 配置

    docker run -d -p 8082:8082 --name two-nginx -v ~/workplace/nginx/html:/usr/share/nginx/html -v ~/wor ...

  4. JavaScript基础09——事件驱动

    1.事件驱动 js控制页面的行为是由事件驱动的.          什么是事件?(怎么发生的)     事件就是js侦测到用户的操作或是页面上的一些行为       事件源(发生在谁身上)     引 ...

  5. vue之子父组件通信

    一. 子-父组件间通信: let children = {    # 定义子组件 template: `<div> <button @click="send"&g ...

  6. C语言博客作业4——数组

    0.展示PTA总分 一维数组 二维数组 字符数组 1.本章学习总结 1.1学习内容总结 一维数组知识点: 无论是对一维数组还是二维数组进行应用时,我们对其下标的应用十分广泛. 1:一维数组的输入和遍历 ...

  7. haproxy 配置文件详解 之 配置文件示例

    此示例文件在haproxy1.8.20 测试没有问题: global log 127.0.0.1 local0 info maxconn user nobody group nobody daemon ...

  8. SQL之存储过程详细介绍及语法(转)

    1:定义 存储过程(stored procedure)是一组为了完成特定功能的SQL语句集合,经编译后存储在服务器端的数据库中,利用存储过程可以加速SQL语句的执行. 存储过程分为系统存储过程和自定义 ...

  9. 特征值、特征向量与PCA算法

    一.复习几个矩阵的基本知识 1. 向量 1)既有大小又有方向的量成为向量,物理学中也被称为矢量,向量的坐标表示a=(2,3),意为a=2*i + 3*j,其中i,j分别是x,y轴的单位向量. 2)向量 ...

  10. javaWeb如何转发数据,jsp页面如何接收?

      1.情景展示 现在有一个需求就是:请求达到服务器后,服务器需要将请求的数据传给另一个页面,如何实现? 2.目标分析 通过服务器跳转到页面上,有两种实现方式:一种是转发,一种是重定向. 我们知道转发 ...