线程池的基础使用

线程池的基础使用

  • 概念
      • 使用线程池的原因:
      • 使用线程池的优点
      • 使用线程池的应用场地介绍
    • 线程池ThreadPoolExecutor
    • 自定义线程池

概念

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

使用线程池的原因:

使用线程池最大的原因就是可以根据系统的需求和硬件的环境灵活的控制线程的数量,并且可以对所有的线程进行统一的管理和控制,从而提高系统的运行效率,降低系统运行压力。

使用线程池的优点

1、线程和任务分离,提升线程的复用性。

2、控制线程并发数量,降低服务器压力,统一管理所有的线程

3、提升系统响应速度,加入创建一个线程时间为T1,执行任务的时间为T2,销毁线程的时间为T3,如果使用线程,可以省略T1和T3。

使用线程池的应用场地介绍

1、网购商品秒杀。

2、云盘支持文件上传和下载

3、票务网购票系统

总结:只要有并发的地方,任务数量或大或小,每个任务执行时间或长或短都可以使用线程池,只不过使用线程池的时候,注意一下设置合理的线程池大小。

线程池ThreadPoolExecutor

使用内置线程池:ThreadPoolExecutor();
解析构造方法

//构造方法
public ThreadPoolExecutor(
	int corePoolSize,//核心线程数
    int maximumPoolSize,//最大线程数
    long keepAliveTime,//最大空闲时间
    TimeUnit unit,//时间单位
    BlockingQueue<Runnable> workQueue,//任务队列
    ThreadFactory threadFactory,//线程工厂
    RejectedExecutionHandler handler//饱和处理机制。
)
/*
	对上面的参数进行解释
	1、corePoolSize:核心线程数,当有任务提交到线程池之后,如果当前运行的线程数量没有达到核心线程数量,新开一个线程来执行任务。如果达到了核心线程数量,可以通过最大线程数量。只负责初始化线程池的时候或者提交任务的时候,允许创建出来的线程数量。
	2、maximumPoolSize:最大线程数量
	3、keepAliveTime:最大的空闲时间,空闲线程最大存活时间,超过空闲时间,线程池将回收这些线程。
	4、TimeUnit:是一个枚举类型,
	5、BlockingQueue:任务队列,理解成一个集合,当我们的线程数量达到核心线程数量之后,如果再有任务提交到线程池,不会立马创建新线程,会把任务放到任务队列中,只有任务队列存满了,按照设定的最大线程数量创建新的线程。
	6、ThreadFactory:线程工厂,允许自己参与创建线程的过程,用自己的方式创建线程,实现接口即可。
	7、RejectedExecutionHandler:饱和处理机制,当任务数量达到核心线程总数量,最大线程数量也创建完了,任务队列也存满了,还有任务的时候,线程池已经容纳不了这么多任务,就给出一个饱和处理机制,
*/

自定义线程池

自定义线程池-参数的分析

  • 核心线程数量:corePoolSize
  • 任务队列长度:workQueue
  • 最大线程数:maximumPoolSize
  • 最大空闲时间:keepAliveTime

自定义线程池实现的步骤

  • 编写任务类MyTask,实现Runnable接口
  • 编写线程类MyWorker,用于执行任务,需要持有所有任务
  • 编写线程池类MyThreadPool,包含提交任务,执行任务的能力
  • 编写测试类MyTest,创建爱你线程池对象,提交多个任务测试

编写任务类

/**
 * @author Alon
 * @title: MyTask
 * @projectName PoolExecutorDemo
 * @description: 任务类,为了看清楚谁在执行,需要定义一个编号,并且实现Runnable接口
 * @date 2020/8/16 20:38
 */
public class MyTask implements Runnable{
     
    //属性:记录任务的编号
    private int id;
    //定义一个构造方法,用于初始化任务编号
    public MyTask(int id){
     
        this.id = id;
    }
    //重写run方法
    @Override
    public void run() {
     
        //获取线程的名称
        String name = Thread.currentThread().getName();
        System.out.println("线程:"+name+"即将执行的任务编号:"+id);
        try {
     
            Thread.sleep(200);
        } catch (InterruptedException e) {
     
            e.printStackTrace();
        }
        System.out.println("线程:"+name+"完成任务编号:"+id);
    }
    //重写toString方法
    @Override
    public String toString() {
     
        return "MyTask{" +
                "id=" + id +
                '}';
    }
}

编程线程类

/**
 * @author Alon
 * @title: MyWorker
 * @projectName PoolExecutorDemo
 * @description: 编写一个线程类,用于执行线程任务,需要定义一个变量来记录线程的名字
 *              需要定义一个集合容器,用来存储任务列表
 *              线程的执行,需要继承Thread类
 * @date 2020/8/16 20:49
 */
public class MyWorker extends Thread{
     
    //定义一个属性,用于记录任务线程名称
    private String name;
    //定义一个属性,是一个容器,用于存储任务列表
    private List<Runnable> tasks;
    //使用构造方法,用于初始化name属性和容器tasks实行
    public MyWorker(String name,List<Runnable> tasks){
     
        this.name = name;
        this.tasks = tasks;
    }
    //重写run方法
    public void run(){
     
        //判断是否任务列表中是否有任务,如果又任务,就一直执行
        if(tasks.size() > 0){
     
            //执行第一个任务,并且删除第一个任务
            Runnable r = tasks.remove(0);
            //运行run方法
            r.run();
        }
    }
}

编写一个线程池类

/**
 * @author Alon
 * @title: MyThreadPool
 * @projectName PoolExecutorDemo
 * @description:自定义线程池
 *          成员属性:
 *              1、需要集合容器,用来存储任务队列,需要保线程安全
 *              2、需要当前线程数
 *              3、需要核心线程数
 *              4、需要最大线程数
 *              5、任务队列长度
 *          成员方法:
 *              1、提交任务,提交后会存入任务队列
 *              2、执行任务,将一个一个的执行任务
 * @date 2020/8/16 20:55
 */
public class MyThreadPool {
     
    //定义集合容器
    private List<Runnable> tasks = Collections.synchronizedList(new LinkedList<>());
    //定义属性记录当前线程数
    private int number;
    //定义属性,记录核心线程数
    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;
    }

    //提交任务的方法
    public void submit(Runnable r){
     
        //判断当前集合任务的数量是否超过了最大任务数量
        if(tasks.size() >= workSize){
     
            System.out.println("任务:"+r+"被丢弃了");
        }else{
     
            //添加到队列中
            tasks.add(r);
            //执行任务
            executeTask(r);
        }
    }
    //执行任务
    public void executeTask(Runnable r){
     
        //判断线程池中的线程数量有没有超过核心线程数
        if(number <= corePoolSize){
     
            //创建核心线程进行执行任务
            new MyWorker("核心线程:"+number,tasks).start();
            number++;
        }else if(number < maxSize){
     
            //超过了核心线程数,创建非核心线程数进行执行
            new MyWorker("非核心线程"+number,tasks).start();
            number++;
        }else{
     
            System.out.println("任务"+r+"被缓存了");
        }
    }
}

测试类

/**
 * @author Alon
 * @title: MyTest
 * @projectName PoolExecutorDemo
 * @description: 测试自定义线程类
 * @date 2020/8/16 23:07
 */
public class MyTest {
     
    public static void main(String[] args) {
     
        //创建线程池类
        MyThreadPool pool = new MyThreadPool(2,4,20);
        //循环创建线程任务
        for(int i = 0;i < 30;i++){
     
            //创建任务
            MyTask mt = new MyTask(i);
            //向线程池里面添加任务
            pool.submit(mt);
        }
    }
}

到这里线程的基础使用就结束了

你可能感兴趣的:(java,java,多线程)