线程池

概念

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

优点

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

线程和任务分离,提升线程重用性

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

提升系统响应速度,加入创建线程用的时间为T1,执行任务所用的时间为T2,销毁线程的时间为T3,那么使用线程池就免去了T1和T3的时间

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

内置线程池

ThreadPoolExecutor

构造方法

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

核心参数

corePoolSize 线程池核心线程大小

线程池中维护的一个最少的线程数量,即使这些线程处于空闲状态,他们也不会销毁,除非设置了allowCoreThreadTimeOut

maximunPoolSize线程池最大线程数量

一个任务被提交到线程池后,会首先放到工作队列中,如果工作队列满了,则会创建一个新的线程,然后从工作队列中取出一个任务交给新线程处理,而将刚提交上来的任务放到工作队列中,线程池最大的线程数量由maximumPoolSize来指定

keepAliveTime空闲线程存活时间

一个线程如果处于空闲状态,并且当前线程的数量大于corePoolSize,那么在指定时间后,这个空闲线程将被销毁,这个指定时间就是keepAliveTime

unit空闲线程存活时间单位

keepAliveTime的计量单位,是一个枚举java.util.concurrent.TimeUnit

workQueue工作队列

新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务,jdk一共提供了四种工作队列

  1. ArrayBlockingQueue 数组型阻塞队列:数据结构,初始化时传入大小,有界,FIFO(先进先出),使用一个重入锁,默认使用非公平锁,入队和出队共用一个锁,互斥
  2. LinkedBlockingQueue链表型阻塞队列:链表结构,默认初始化大小为Integer.MAX_VALUE,有界(近似无界).FIFO,使用俩个重入锁分别控制元素的入队和出队,使用condition进行线程之间的唤醒和等待
  3. SynchronousQueue同步队列:容量为0,添加任务必须等待取出任务,这个队列相当于通道,不存储元素
  4. PriorityBlockingQueue 有线阻塞队列:无界,默认采用元素自然顺序升序排序
  5. delayQueue延时队列,无界,元素有过期时间,过期的元素才能被取出

threadFactory线程工厂

创建新线程的时候使用的工厂,可以用来指定线程名,是否为daemon线程等

handler拒绝策略

当提交任务数大于 corePoolSize 的时候,会优先将任务放到 workQueue 阻塞队列中。当阻塞队列饱和后,会扩充线程池中线程数,直到达到 maximumPoolSize 最大线程数配置。此时,再多余的任务,则会触发线程池的拒绝策略了。总结起来,也就是一句话,当提交的任务数大于(workQueue.size() + maximumPoolSize ),就会触发线程池的拒绝策略。

  1. AbortPolicy:丢弃任务并抛出RejectedExecutionException异常(默认的拒绝策略)
  2. DiscardPolicy:丢弃任务但是不抛出异常,可能导致无法发现系统的异常状态
  3. DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务
  4. CallerRunPolicy:由调用线程处理该任务

上述的是一般的设计原则,并不是固定的,可以根据实际情况灵活调整

ExecutorService

ExecutoService接口是java内置的线程池接口,通过学习接口中的方法,可以快速掌握java内置线程池的基本使用

常用方法

void shutdown():启动一次顺序关闭,执行以前的任务,但不接受新任务
List<Runnable>shutdownNow();停止所有正在执行的任务,暂停处理正在等待的任务,并返回执行的任务列表
<T>future<T>sumbit(callable <T> task)执行待返回值的任务,返回一个future对象
Future<?>submit(Runnable task)执行Runnable任务,并返回一个表示该任务的Future
<T>Future<T>submit(Runnable task,T result)执行Runnable任务,并返回一个表示该任务的Future

Executors

提交任务

package demo2;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

public class MyTest01 {
    public static void main(String[] args) {
//        test1();
//        test2();
//        test3();
//        test4();
        test5();
    }

    private static void test1() {
        //使用工厂类获取线程池对象
        ExecutorService es= Executors.newCachedThreadPool();
        //提交任务
        for (int i=1;i<=10;i++){
            es.submit(new MyRunnale(i));
        }
    }
    //自定义线程名称
    private static void test2() {
        //使用工厂类获取线程池对象
        ExecutorService es= Executors.newCachedThreadPool(new ThreadFactory() {
            int n=1;
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r,"自定义线程名称"+n++);
            }
        });
        //提交任务
        for (int i=1;i<=10;i++){
            es.submit(new MyRunnale(i));
        }
    }
    private static void test3() {
        //使用工厂类获取线程池对象
        ExecutorService es= Executors.newFixedThreadPool(3);//传入参数指定线程池最多三个线程
        //提交任务
        for (int i=1;i<=10;i++){
            es.submit(new MyRunnale(i));
        }
    }
    private static void test4() {
        //使用工厂类获取线程池对象
        ExecutorService es= Executors.newFixedThreadPool(3,new ThreadFactory() {//传入参数指定线程池最多三个线程
            int n=1;
            @Override
            public Thread newThread(Runnable r) {
                //指定线程名
                return new Thread(r,"自定义线程名称"+n++);
            }
        });
        //提交任务
        for (int i=1;i<=10;i++){
            es.submit(new MyRunnale(i));
        }
    }
    private static void test5() {
        //使用工厂类获取线程池对象
        ExecutorService es= Executors.newSingleThreadExecutor();//只让一个线程执行
        //提交任务
        for (int i=1;i<=10;i++){
            es.submit(new MyRunnale(i));
        }
    }
    private static void test6() {
        //使用工厂类获取线程池对象
        ExecutorService es= Executors.newSingleThreadExecutor(new ThreadFactory() {
            int n=1;
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r,"自定义线程名称"+n++);
            }
        });
        //提交任务
        for (int i=1;i<=10;i++){
            es.submit(new MyRunnale(i));
        }
    }


}
class MyRunnale implements Runnable{
    private int id;
    MyRunnale(int id){
        this.id=id;
    }
    @Override
    public void run() {
        //获取线程的名称,打印一句话
        String name = Thread.currentThread().getName();
        System.out.println(name+"执行了任务...."+id);
    }
}

结束线程池

package demo2;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

public class MyTest01 {
    public static void main(String[] args) {
//        test1();
        test2();
    }

    private static void test1() {
        //使用工厂类获取线程池对象
        ExecutorService es= Executors.newCachedThreadPool();
        //提交任务
        for (int i=1;i<=10;i++){
            es.submit(new MyRunnale(i));
        }
        //关闭线程池,以前的任务还是会继续执行
        es.shutdown();
//        es.submit(new MyRunnale(888));//写这行会报异常
    }
    private static void test2() {
        //使用工厂类获取线程池对象
        ExecutorService es= Executors.newCachedThreadPool();
        //提交任务
        for (int i=1;i<=10;i++){
            es.submit(new MyRunnale(i));
        }
        //立刻关闭线程池,如果线程池里还有缓存没有执行,则取消执行,并返回这些任务
        List<Runnable> list = es.shutdownNow();
        System.out.println(list);
    }


}
class MyRunnale implements Runnable{
    private int id;
    MyRunnale(int id){
        this.id=id;
    }
    @Override
    public void run() {
        //获取线程的名称,打印一句话
        String name = Thread.currentThread().getName();
        System.out.println(name+"执行了任务...."+id);
    }

    @Override
    public String toString() {
        return "MyRunnale{" +
                "id=" + id +
                '}';
    }
}
Last modification:December 20, 2022
如果觉得我的文章对你有用,请随意赞赏