文章目录

  • 一、线程池
  • 二、线程池的8个参数
  • 三、JDK中4种内置的拒绝策略
  • 四、自定义线程工厂
  • 五、自定义拒绝策略
  • 六、任务实现的接口
  • 6.1、Runnable
  • 6.2、Callable
  • 七、线程池新任务处理流程
  • 八、线程池实例
  • 九、线程数的设定
  • 十、JDK默认线程池
  • 10.1、CacheThreadPool
  • 10.2、FixedThreadPool
  • 10.3、SingleThreadExecutor
  • 10.4、ScheduledThreadPool


一、线程池

java 线程池参数有哪些 java四种线程池参数详解_java


java 线程池参数有哪些 java四种线程池参数详解_java_02

二、线程池的8个参数

  • corePoolSize: 核心线程数,默认不会被回收
  • maximumPoolSize: 最大线程数
  • keepAliveTime: 非核心线程存活时间
  • TimeUnit: 非核心线程存活时间的时间单位
  • BlockingQueue: 存储任务的阻塞队列
  • ThreadFactory: 生产线程的工厂
  • RejectedExecutionHandler: 拒绝策略

三、JDK中4种内置的拒绝策略

  • CallerRunsPolicy: submit任务的线程处理,如果是main线程调用了submit,则有main线程处理任务
  • AbortPolicy 丢弃任务并抛出RejectedExecutionException异常
  • DiscardPolicy 直接丢弃任务,不抛出异常
  • DiscardOldestPolicy 丢弃存放在任务队列中最久的任务

四、自定义线程工厂

自定义线程工厂需要实现接口ThreadFactory重写Thread newThread(Runnable r);方法。

class MyThreadFactory implements ThreadFactory{
    int count = 0;
    @Override
    public Thread newThread(Runnable r) {
        System.out.println("创建线程");
        return new Thread(r, "我的自定义线程池创建的线程" + (++count));
    }
}

五、自定义拒绝策略

自定义拒绝策略需要实现接口RejectedExecutionHandler重写void rejectedExecution(Runnable r, ThreadPoolExecutor executor)方法。

class MyRejectStrategy implements RejectedExecutionHandler{

    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        // 一般会将线程池无法处理的任务先放进Kafka
    }
}

六、任务实现的接口

6.1、Runnable

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}
Runnable runnable = () -> {
    try {
        System.out.println("运行线程: " + Thread.currentThread().getName());
        // 阻塞
        int read = System.in.read();
    } catch (IOException e) {
        e.printStackTrace();
    }
};

6.2、Callable

@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}
Callable<String> callable = () -> {
    System.out.println("运行线程: " + Thread.currentThread().getName());
    // 阻塞
    Thread.sleep(5000);
    return "Hello, Callable!";
};

七、线程池新任务处理流程

java 线程池参数有哪些 java四种线程池参数详解_多线程_03

八、线程池实例

import java.io.IOException;
import java.util.concurrent.*;

/**
 * @author IT00ZYQ
 * @date 2021/5/31 23:00
 **/
public class T02_ExecutorService {
    /**
     * 核心线程数
     */
    public static final int CORE_POOL_SIZE = 2;
    /**
     * 最大线程数
     */
    public static final int MAX_POOL_SIZE = 4;

    /**
     * 非核心线程空闲存活时间
     */
    public static final long KEEP_ALIVE_TIME = 60;
    public static void main(String[] args) {
        Runnable runnable = () -> {
            try {
                System.out.println("运行线程: " + Thread.currentThread().getName());
                // 阻塞
                int read = System.in.read();
            } catch (IOException e) {
                e.printStackTrace();
            }
        };
        
        Callable<String> callable = () -> {
            System.out.println("运行线程: " + Thread.currentThread().getName());
            Thread.sleep(5000);
            return "Hello, Callable!";
        };

        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                CORE_POOL_SIZE, 
                MAX_POOL_SIZE,
                KEEP_ALIVE_TIME, 
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(5),
                new MyThreadFactory(),
                new MyRejectStrategy());
        
        // 拒绝策略
        // CallerRunsPolicy submit任务的线程处理,这里由main线程处理
        // AbortPolicy 丢弃任务并抛出RejectedExecutionException异常
        // DiscardPolicy 直接丢弃任务,不抛出异常
        // DiscardOldestPolicy 丢弃存放在任务队列中最久的任务

        int count = 0;
        for (int i = 0; i < 4; i++) {
            System.out.println("提交第" + (++count) + "个任务");
            threadPool.execute(runnable);
        }

        for (int i = 0; i < 5; i++) {
            System.out.println("提交第" + (++count) + "个任务");
            threadPool.submit(callable);
        }


        System.out.println("任务队列任务数: " + threadPool.getQueue().size());
        System.out.println("线程池线程数: " + threadPool.getPoolSize());
        System.out.println("提交第" + (++count) + "个任务");
        threadPool.submit(runnable);
    }
}

class MyThreadFactory implements ThreadFactory{
    int count = 0;
    @Override
    public Thread newThread(Runnable r) {
        System.out.println("创建线程");
        return new Thread(r, "我的自定义线程池创建的线程" + (++count));
    }
}

class MyRejectStrategy implements RejectedExecutionHandler{

    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        // 一般会将线程池无法处理的任务先放进Kafka
        // ...

        System.out.println("执行了拒绝策略");
    }
}

九、线程数的设定

  • CPU 密集型任务:有大量时间用于计算,CPU密集型任务消耗的主要是 CPU 资源,将线程数设置为 CPU 核数 + 1,线程数设定为比CPU核数多一个的原因是当线程发生中断等不需要CPU资源的情况时,CPU资源能得到充分利用。
  • I/O 密集型任务:有大量时间用于I/O,I/O密集型任务大量的时间用于 I/O 交互,而线程进行 I/O 的时间段内不会占用 CPU 资源,为了充分利用CPU资源,将线程数设定为 2 * CPU核数

十、JDK默认线程池

10.1、CacheThreadPool

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

核心线程数为0,最大线程数为Integer.MAX_VALUE,非核心线程空闲存活时间60s,也就是说CachedThreadPool中只要有线程空闲达到60s就会被回收,每次有新的任务加入时,没有空闲线程则创建新的线程去执行任务。

10.2、FixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

核心线程数为nThreads,最大线程数也为nThreads,核心线程数与最大线程数相等,也就是说,一旦线程被创建出来,它就是核心线程,所有线程都不会被回收。

10.3、SingleThreadExecutor

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

核心线程数与最大线程数都为1,线程池只有一个线程,且这个线程不会被回收。
为什么需要单线程的线程池呢?
线程池维护了一个任务队列,使用单线程的线程池时不需要自己维护任务队列。

10.4、ScheduledThreadPool

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

public ScheduledThreadPoolExecutor(int corePoolSize) {
    return new ThreadPoolExecutor(corePoolSize, Integer.MAX_VALUE, 
    0, NANOSECONDS,
    new DelayedWorkQueue());
}

定时任务或延迟任务线程池,用于处理定时/延迟任务。核心线程数为corePoolSize,最大线程数为MAX_VALUE,线程一空闲就会被回收。