代码编织梦想

前言

为什么要用线程池

在一个进程中,线程是一种非常稀缺的资源。频繁地创建或销毁线程也是一个消耗资源的过程,所以用线程池,可以减少以上的过程。
优势:

  1. 线程复用,减少创建和销毁,提高性能
  2. 响应快
  3. 可以统一管理和监控

什么时候用

  1. 任务量大和需要异步
  2. 每个任务处理时间较短

原理

参数

线程池七大参数:

  1. corePoolSize(核心池大小)
  2. maximumPoolSize(最大线程池大小)
  3. keepAliveTime(存活时间)
  4. unit(时间单位)
  5. workQueue(工作队列)
  6. threadFactory(线程工厂)
  7. handler(拒绝策略)

原理图

1.jpg

说明

这里将设定线程池的参数:核心数=2,最大数=3,队列数=5

  1. 如果核心池为空,则有任务进入,马上在核心池中进行
  2. 如果核心池已经满了,则还有任务进入,非核心池将开辟线程进行处理
  3. 如果核心池和非核心池都满了,则还有任务的,进入队列等待
  4. 如果队列也满了,则根据拒绝策略,对想要进入队列的任务进行处理,默认的就是直接报异常
    四个拒绝策略:
    AbortPolicy:
    直接抛异常
java.util.concurrent.RejectedExecutionException: Task com.company.Thread.MyTask@135fbaa4 rejected from java.util.concurrent.ThreadPoolExecutor@45ee12a7

DiscardPolicy:
丢弃任务,但是不抛出异常。

CallerRunsPolicy:
丢弃队列最前面的任务,然后重新提交被拒绝的任务

for (int i = 0; i < 8; i++) {
    try {
        threadPoolExecutor.execute(new MyTask(i));
    }
    catch (Exception e){
        e.printStackTrace();
    }
}
threadPoolExecutor.execute(new MyTask(8));

#######################

pool-1-thread-3--7:done!
pool-1-thread-2--1:done!
pool-1-thread-1--0:done!
pool-1-thread-2--4:done!
pool-1-thread-3--3:done!
pool-1-thread-1--5:done!
pool-1-thread-2--6:done!
pool-1-thread-3--8:done!

可以看出8才是最后提交的,但是却有8的任务完成,其中2不见了,因为2是在队列的最前面。

DiscardOldestPolicy:
调用线程处理该任务

代码分析

构造函数的七大参数

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

有四个可选策略

  1. CallerRunsPolicy
  2. AbortPolicy
  3. DiscardPolicy
  4. DiscardOldestPolicy
    默认策略:AbortPolicy()
private static final RejectedExecutionHandler defaultHandler =
    new AbortPolicy();
...
public static class CallerRunsPolicy implements RejectedExecutionHandler 
...
public static class AbortPolicy implements RejectedExecutionHandler
...
public static class DiscardPolicy implements RejectedExecutionHandler
...
public static class DiscardOldestPolicy implements RejectedExecutionHandler
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
...
public void execute(Runnable command) {
    // 命令为空抛异常
    if (command == null)
        throw new NullPointerException();
    // 原子性cas,线程安全
    int c = ctl.get();
    // 工作数小于核心数,开始正常工作
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 队列满了,进行拒绝
    else if (!addWorker(command, false))
        reject(command);
}

测试

测试代码:

public class threadpool {
    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor threadPoolExecutor =
                new ThreadPoolExecutor(2, 3, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(5), Executors.defaultThreadFactory());
//        ExecutorService e1 = Executors.newCachedThreadPool();
//        ExecutorService e2 = Executors.newFixedThreadPool(10);
//        ExecutorService e3 = Executors.newSingleThreadExecutor();


        for (int i = 0; i < 9; i++) {
            try {
                threadPoolExecutor.execute(new MyTask(i));
            }
            catch (Exception e){
                e.printStackTrace();
            }
        }


        Queue<Runnable> queue = threadPoolExecutor.getQueue();

        for (int i = 0; i < 10; i++) {
            System.out.println("核心数:" + threadPoolExecutor.getCorePoolSize());
            System.out.println("最大数:" + threadPoolExecutor.getMaximumPoolSize());
            System.out.println("池化数:" + threadPoolExecutor.getPoolSize());
            System.out.println("队列数:" + queue.size());
            System.out.println("默认策略:" + threadPoolExecutor.getRejectedExecutionHandler());
            Thread.sleep(60000L);
            System.out.println("*************************************");
        }

        threadPoolExecutor.shutdown();
    }
}

class MyTask implements Runnable{
    int i = 0;

    public MyTask(int i){
        this.i = i;
    }

    @Override
    public void run() {
        try{
            Thread.sleep(60000L);
            System.out.println(Thread.currentThread().getName() + "--" + i + ":done!");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

以下结果可以看出,在核心池和非核心池都满了,和队列也满的情况下,在加入任务,就会触发拒绝策略,报异常。
每完成一个任务,都将队列的任务往核心池仍,直到队列空了。
结果:

java.util.concurrent.RejectedExecutionException: Task com.company.Thread.MyTask@135fbaa4 rejected from java.util.concurrent.ThreadPoolExecutor@45ee12a7[Running, pool size = 3, active threads = 3, queued tasks = 5, completed tasks = 0]
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
	at com.company.Thread.threadpool.main(threadpool.java:17)
核心数:2
最大数:3
池化数:3
队列数:5
默认策略:java.util.concurrent.ThreadPoolExecutor$AbortPolicy@2503dbd3
*************************************
pool-1-thread-1--0:done!
pool-1-thread-2--1:done!
pool-1-thread-3--7:done!
核心数:2
最大数:3
池化数:3
队列数:2
默认策略:java.util.concurrent.ThreadPoolExecutor$AbortPolicy@2503dbd3
pool-1-thread-1--2:done!
*************************************
核心数:2
最大数:3
池化数:3
pool-1-thread-3--4:done!
pool-1-thread-2--3:done!
队列数:1
默认策略:java.util.concurrent.ThreadPoolExecutor$AbortPolicy@2503dbd3
*************************************
核心数:2
最大数:3
pool-1-thread-1--5:done!
pool-1-thread-3--6:done!
池化数:2
队列数:0
默认策略:java.util.concurrent.ThreadPoolExecutor$AbortPolicy@2503dbd3
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_43163694/article/details/122763111

java线程池实现原理详解_疯狂哈丘的博客-爱代码爱编程_java线程池原理

文章目录 原理概述线程池的几个主要参数的作用任务提交后的流程分析源码解析1. 提交任务相关源码2. Worker的结构3. 添加Callable任务的实现源码4. shutdown和shutdownNow方法的实现

java线程池原理剖析_不清不慎的博客-爱代码爱编程

一般情况下,我们经常会创建大量的线程为我们完成分配的工作,但是有时候频繁的创建销毁线程也会给系统带来很大的损耗,因此,为了避免带给系统不必要的损耗和重复造轮子,我们引入了线程池的概念。本篇文章主要讲解线程的实现原理。

深入理解java线程池原理分析与使用_第一堂课的博客-爱代码爱编程

首先介绍如何使用,后面再介绍原理: 第一种: Java1.5以后自带的线程池 public class App { public static void main(String[] args) throws Exception {          ExecutorService executorService = new ThreadPoo

Java线程池原理-爱代码爱编程

Java线程理解:   线程是调度CPU的最小单元,也叫轻量级进程LMP(Light Weight Process)。 两种线程模型: 用户级线程(ULT):用户程序实现,不依赖操作系统核心,应用提供创建、同步、调度和管理线程的函数来控制用户线程。 不需要用户态/内核态切换,速度快。内核对ULT无感知,线程阻塞则进程(包括它的所有线程)阻塞。内核级线

java线程池原理_面试必问:java线程池实现原理-爱代码爱编程

处理器早已迈入多核心时代,为了充分利用cpu多核资源,应用都会采用多线程并行/并发计算,最大限度的利用多核提升应用程序性能。然而线程的创建是有代价的,一方面需要申请内存资源,另一方面需要操作系统内核把线程加入调度队列,开销是比较大的,这在高并发系统中性能隐患非常大,另一方面线程需要消耗内存空间,如果进程创建的线程数量不加以控制,很有可能会耗尽进程的内存

java线程池的工作原理_JAVA线程池原理详解一-爱代码爱编程

线程池的优点 1、线程是稀缺资源,使用线程池可以减少创建和销毁线程的次数,每个工作线程都可以重复使用。 2、可以根据系统的承受能力,调整线程池中工作线程的数量,防止因为消耗过多内存导致服务器崩溃。 线程池的创建 1 public ThreadPoolExecutor(int corePoolSize, 2 int maxim

java线程池原理简答_java线程池原理及实现方式-爱代码爱编程

线程池的定义 线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程 为什么要使用线程池 1、减少在创建和销毁线程上所花的时间以及系统资源的开销 2、在一个 JVM 里创建太多的线程可能会导致系统由于过度消耗内存而用完内存或“切换过度”。为了防止资源不足,服务器应用程序需要一些办法来限制

java 服务器 线程池_java线程池原理-爱代码爱编程

参考文章: 作者:AKyS佐毅 链接:https://www.jianshu.com/p/f5ead62827d7 来源:简书 线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提高服务器程序性能的。它把T1,T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1,T3的开销了。

Java线程池原理和使用-爱代码爱编程

什么是线程池? 线程池是Java线程的一种使用模式,通过池的思想对线程的创建和使用进行统一的管理。 为什么要用线程池? Java线程是稀缺资源,频繁的创建和销毁会对CPU带来一定的开销,线程过多也会带来调度开销,不易维护和管理,进而影响缓存局部性和整体的性能。使用线程池可以对线程进行复用,避免了在处理短时间任务时创建与销毁线程的代价,还能防止过分调度

Elasticsearch实战之搜索项目-爱代码爱编程

📢📢📢📣📣📣  哈喽!大家好,我是【一心同学】,一位上进心十足的【Java领域博主】!😜😜😜 ✨【一心同学】的写作风格:喜欢用【通俗易懂】的文笔去讲解每一个知识点,而不喜欢用【高大上】的官方陈述。 ✨【一心同学】博客的领域是【面向后端技术】的学习,未来会持续更新更多的【后端技术】以及【学习心得】。 ✨如果有对【后端技术】感