1.java默认提供的线程池类型

newCachedThreadPool:创建一个可缓存的线程池,线程池容量为无限大,当第二个任务开始时第一个任务已经完成,则复用之前的线程,不会创建新线程,可灵活回收线程。

newFixedThreadPool:创建一个有固定代销的线程池,可控制最大并发数(线程数量),超出的线程会在等待队列中等待。

newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行。

newSingleThreadExecutor:创建一个单线程化的线程池,它只有唯一的一个工作线程来执行任务,所有任务都只能按照指定的顺序执行。

2.创建线程池的正确姿势

使用java默认提供的线程池的创建方法是可以选择的方案,但是在有的时候会出现内存溢出的错误。

// 创建对应功能的线程池
ExecutorService executor1 = Executors.newCachedThreadPool();
ExecutorService executor2 = Executors.newFixedThreadPool(2);
ExecutorService executor3 = Executors.newScheduledThreadPool(4);
ExecutorService executor4 = Executors.newSingleThreadExecutor();
​
// 调用线程池执行相应任务
executor1.execute(new Runnable() {
    @Override
    public void run() {
        for (int i = 0; i < 100000; i++) {
            System.out.println(Thread.currentThread().getName() + "  执行  " + i);
        }
    }
});

推荐的创建线程池的方式:

ExecutorService executorService = new ThreadPoolExecutor(10, 10, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10));

即使用new ThreadPoolExecutor()并自己初始化响应的参数。下面介绍一下各个参数的意义。

ThreadPoolExecutor的构造函数:

public ThreadPoolExecutor(int corePoolSize,
    int maximumPoolSize,
    long keepAliveTime,
    TimeUnit unit,
    BlockingQueue<Runnable> workQueue,
    RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
        Executors.defaultThreadFactory(), handler);
}

corePoolSize:线程池核心线程数量,也就是即使空闲也不会被销毁的线程。

maximumPoolSize:线程池最大线程数量。

keepAliveTime:空闲线程存活时间,如果当前线程处于空闲状态且线程数量大于corePoolSize,那么这个线程将会被销毁。

unit:keepAliveTime的计量单位。

BlockingQueue:阻塞队列,即当一个任务进入到线程池后如果现在没有空闲的核心线程,就会被添加到阻塞队列中等待直到有核心线程空闲。而当阻塞队列也满了之后才会调用核心线程之外,最大线程之内的空闲线程。

3.线程池的拒绝策略

当线程池中的等待队列和最大空闲线程都满了的时候,就会执行事先设定好的拒绝策略,java提供了4种拒绝策略。

1.CallerRunsPolicy:该策略下,在调用者线程中直接执行被拒绝人物的run()方法,除非线程池已经shutdown,则直接抛弃任务。

2.AbortPolicy:该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。

3.DiscardPolicy:该策略下,直接丢弃任务,什么都不做。

4.DiscardOldestPolicy:该策略下,抛弃队列最早的那个任务,然后尝试把这次拒绝的任务放入队列中。

4.线程池的工作过程

  • 一个线程任务到达线程池
  • 检查当前的核心线程是否有空闲线程,如有则直接使用空闲的核心线程执行任务
  • 如果当前核心线程全为使用状态,则将当前任务添加到等待队列中
  • 如果当前核心线程与等待队列均为满状态,则调用非核心线程执行任务
  • 如果当前核心线程,等待队列与非核心线程均为满状态,则执行线程池的拒绝策略。

5.直接使用Executors创建线程池会有什么问题?

如果我们使用java默认提供的Executors创建线程池。
当我们创建FixedThreadPool 和 SingleThreadExecutor的时候,因为默认的创建方法的参数中阻塞队列的长度为Integer.MAX_VALUE,这就会导致当任务数量很多的时候队列堆积大量的任务请求造成内存溢出(OOM)。
当我们创建CachedThreadPool 和 ScheduledThreadPool的时候,因为默认的创建方法的参数中核心线程的大小为Integer.MAX_VALUE,这就会导致当任务数量很多的时候创建大量的线程,造成内存溢出(OOM)。
上面两个问题的核心就是在于默认提供的参数大小往往不是合适的,因此才建议我们使用new ThreadPoolExecutor的方法创建线程池,并手动初始化各参数。


超人不会飞
12 声望4 粉丝

一个想去做开发的研究生