编程知识 cdmana.com

Java之线程池

线程池

线程池基本概念

​ 线程池其实就是一种多线程处理形式,处理过程中可以将任务添加到队列中,然后在创建线程后自动启动这些任务。这里的线程就是我们前面学过的线程,这里的任务就是我们前面学过的实现了Runnable或Callable接口的实例对象;

​ 使用线程池最大的原因就是可以根据系统的需求和硬件环境灵活的控制线程的数量,且可以对所有线程进行统一的管理和控制,从而提高系统的运行效率,降低系统运行运行压力;当然了,使用线程池的原因不仅仅只有这些,我们可以从线程池自身的优点上来进一步了解线程池的好处;

线程池优势

1:线程和任务分离,提升线程重用性;
2:控制线程并发数量,降低服务器压力,统一管理所有线程;
3:提升系统响应速度,假如创建线程用的时间为T1,执行任务用的时间为T2,销毁线程用的时间为T3,那么使用线程池就免去了T1和T3的时间;

线程池应用场景

应用场景介绍

  • 网购商品秒杀
  • 云盘文件上传和下载
  • 12306网上购票系统等
    只要有并发的地方、任务数量大或小、每个任务执行时间长或短的都可以使用线程池;只不过在使用线程池的时候,注意一下设置合理的线程池大小即可;

线程池使用

Java内置线程池

jdk1.5版本之后才有线程池 ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize, //核心线程数量
                              int maximumPoolSize,// 最大线程数
                              long keepAliveTime, // 最大空闲时间
                              TimeUnit unit,         // 时间单位
                              BlockingQueue<Runnable> workQueue,   // 任务队列
                              ThreadFactory threadFactory,    // 线程工厂
                              RejectedExecutionHandler handler  // 饱和处理机制
	) 
{
     ... }

corePoolSize(核心线程数):当一个任务提交到线程池中时,如果当前运行的线程数量还没达到核心线程数时,就会新开启一个线程来执行当前任务。当超过核心线程数时,需要结合最大线程数以及任务队列等参数。

maximumPoolSize(最大线程数):控制程序中允许创建出来的最大的线程数,从而可以保证系统能正常运行,保证服务器能运行在一定的压力之下;

keepAliveTime(最大空闲时间):线程的空闲时间

unit(时间单位):空闲时间的单位,是个枚举;

workQueue(任务队列):当核心线程满了之后,新提交进来的任务就会被加入到任务队列中,当任务队列也满了,此时,才会按照最大线程数来创建新的线程。相当于临时缓冲区;

threadFactory(线程工厂):创建线程的工厂。

handler(饱和处理机制):拒绝策略,当我们的核心线程数满了,最大线程数也满了,任务队列也满了之后,会执行拒绝策略。

线程池工作流程

在这里插入图片描述

自定义线程池

参数设计分析

核心线程数(corePoolSize)

​ 核心线程数的设计需要依据任务的处理时间和每秒产生的任务数量来确定,例如:执行一个任务需要0.1秒,系统百分之80的时间每秒都会产生100个任务,那么要想在1秒内处理完这100个任务,就需要10个线程,此时我们就可以设计核心线程数为10;当然实际情况不可能这么平均,所以我们一般按照8020原则设计即可,既按照百分之80的情况设计核心线程数,剩下的百分之20可以利用最大线程数处理;

任务队列长度(workQueue)

​ 任务队列长度一般设计为:核心线程数/单个任务执行时间X2即可;例如上面的场景中,核心线程数设计为10,单个任务执行时间为0.1秒,则队列长度可以设计为200;

最大线程数(maximumPoolSize)

​ 最大线程数的设计除了需要参照核心线程数的条件外,还需要参照系统每秒产生的最大任务数决定:例如:上述环境中,如果系统每秒最大产生的任务是1000个,那么,最大线程数=(最大任务数-任务队列长度)X单个任务执行时间;既: 最大线程数=(1000-200)*0.1=80个;

最大空闲时间(keepAliveTime)

​ 这个参数的设计完全参考系统运行环境和硬件压力设定,没有固定的参考值,用户可以根据经验和系统产生任务的时间间隔合理设置一个值即可;

自定义线程池实现

任务类

package com.test.demo01;

/** * 任务类 * * @author zhangzengxiu * @date 2022/9/22 */
public class MyTask implements Runnable {
    

    private int id;

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

    public void run() {
    
        String threadName = Thread.currentThread().getName();
        System.out.println("线程:" + threadName + ",即将执行任务:" + id);
        try {
    
            Thread.sleep(200);
        } catch (InterruptedException e) {
    
            e.printStackTrace();
        }
        System.out.println("线程:" + threadName + ",完成执行完成任务:" + id);
    }

    @Override
    public String toString() {
    
        return "MyTask{" +
                "id=" + id +
                '}';
    }
}

线程类

package com.test.demo01;

import java.util.List;

/** * 线程类 * 保存线程名称的属性; * 设计一个集合,用于保存所有的任务 * * @author zhangzengxiu * @date 2022/9/22 */
public class MyWorker extends Thread {
    

    /** * 线程名称 */
    private String name;

    /** * 任务集合 */
    private List<Runnable> tasks;

    public MyWorker(String name, List<Runnable> tasks) {
    
        super(name);
        this.tasks = tasks;
    }

    @Override
    public void run() {
    
        while (tasks.size() > 0) {
    
            Runnable runnable = tasks.remove(0);
            runnable.run();
        }
    }
}

线程池类

package com.test.demo01;

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

/** * 自定义线程池 * 成员变量: * 1、任务队列:线程安全集合 * 2、当前线程数 * 3、核心线程数 * 4、最大线程数 * 5、任务队列长度 * 成员方法: * 1、提交:将任务提交到集合中 * 2、执行任务:判断核心还是非核心线程 * * @author zhangzengxiu * @date 2022/9/22 */
public class MyThreadPool {
    

    /** * 任务队列 */
    private List<Runnable> tasks = Collections.synchronizedList(new LinkedList<Runnable>());

    /** * 当前线程数 */
    private int num;

    /** * 核心线程数 */
    private int corePoolSize;

    /** * 最大线程数 */
    private int maxSize;

    /** * 任务对列长度 */
    private int workSize;

    public MyThreadPool(int corePoolSize, int maxSize, int workSize) {
    
        this.corePoolSize = corePoolSize;
        this.maxSize = maxSize;
        this.workSize = workSize;
    }

    /** * 提交任务到线程池 * * @param runnable */
    public void submit(Runnable runnable) {
    
        if (tasks.size() >= workSize) {
    
            System.out.println("任务被丢弃:" + runnable);
        } else {
    
            tasks.add(runnable);
            //执行任务
            executeTask(runnable);
        }
    }

    /** * 执行任务 * * @param runnable */
    public void executeTask(Runnable runnable) {
    
        //当前线程数是否小于核心线程数
        if (num < corePoolSize) {
    
            new MyWorker("核心线程:" + num, tasks).start();
            num++;
        } else if (num < maxSize) {
    
            //小于最大线程数
            new MyWorker("非核心线程:" + num, tasks).start();
            num++;
        } else {
    
            System.out.println("任务被缓存:" + runnable);
        }
    }

}

测试类

package com.test.demo01;

/** * 测试类 * * @author zhangzengxiu * @date 2022/9/22 */
public class MyTest {
    

    public static void main(String[] args) {
    
        //核心线程
        int coreSize = 2;
        //最大线程数
        int maxSize = 4;
        //任务队列
        int workSize = 20;
        MyThreadPool pool = new MyThreadPool(coreSize, maxSize, workSize);
        //提交多个任务
        for (int i = 1; i <= 100; i++) {
    
            pool.submit(new MyTask(i));
        }
    }

}

Java内置线程池

异步计算结果(Future)

线程池工作原理

自定义线程池

Java内置线程池

综合案例

版权声明
本文为[zhangzengxiu]所创,转载请带上原文链接,感谢
https://blog.csdn.net/zhangzengxiu/article/details/126963997

Scroll to Top