JUC线程池学习

70 阅读3分钟

为什么使用线程池

使用线程池可以复用线程、管理资源,避免频繁创建、销毁线程带来的性能开销。

  • 线程创建、开销对系统资源开销较大。
  • 使用线程池,可以预先创建一定数量的线程,放入线程池,需要执行任务可以从线程池中获取复用线程。

线程池参数(ThreadPoolExecutor)

  • corePoolSize核心线程数量

    线程池中长久存活的线程

  • maximumPoolSize最大线程数量

    线程池中最大线程数量,临时线程用完销毁

  • keepAliveTime临时线程最大存活时间

  • unit临时线程最大存活时间单位

  • workQueue任务阻塞队列

    提交任务数超出核心线程数之后,再提交的任务就存放在工作队列

  • threadFactory线程工厂 创建线程的工厂类

  • handler任务拒绝策略

线程池参数设置

线程数量不是越多越好,频繁的线程上下文交换会影响系统性能。

Runtime.getRuntime().avilableProcessors()获取CPU最大核心数

  • CPU密集型:任务需要大量计算,很少阻塞,cpu一直处于忙碌状态 无法切换线程上下文。CPU核数 + 1。
  • I/O密集型:任务涉及大量IO操作,在IO阻塞等待时,cpu可以切换线程上下文处理其他任务。2 * CPU核数。

线程池提交任务流程

  • 提交任务,线程数小于corePoolSize 创建核心线程执行任务。
  • 任务数量超过corePoolSize,后续任务就进入阻塞队列等待核心线程获取执行。
  • 阻塞队列也满了,后续创建 maximumPoolSize - corePoolSize个临时线程来执行任务。任务执行完成,临时线程等待keepAliveTime之后自动销毁。
  • 任务达到maximumPoolSize,阻塞队列还是满的,根据不同的拒绝策略处理。
    • AbortPolicy 抛出RejectedExecutionException,新任务立即拒绝,不会加入阻塞队列,也不会执行,适合运行部分任务失败的场景。
    • DiscardPolicy 丢弃新任务不抛出异常,适合允许丢失少量任务的场景。
    • DiscardOldestPolicy 将阻塞队列最早的任务删除,强调新任务更重要 强调实时性。
    • CallerRunsPolicy 将任务回退给调用线程,不抛出异常,适合任务不丢失 接受任务延迟的场景。

image.png

Executor创建线程池存在问题

Executor是一个工厂类,提供了创建不同线程池的静态方法:newCachedThreadPoolnewFixedThreadPoolnewSingleThreadPoolExecutornewScheduledThreadPool

Executor使用起来比较方便,但是可能存在一些问题:

  • 固定线程数量和单个线程的线程池,线程数量不会溢出,但是任务阻塞队列可能溢出,可能堆积大量任务,导致OOM。
  • newCachedThreadPoolnewScheduledThreadPool 线程数量可能溢出,创建大量线程,导致OOM。

线程池不要使用Executor创建,而是通过ThreadPoolExecutor创建,规避资源耗尽的风险。

自定义线程池

public class MyThreadPool {
    
    private fianl int corePoolSize;
    
    private final int maxSize;
    
    private final int timeout;
    
    private fianl TimeUnit timeUnit;
    
    private final BlockingQueue<Runnable> blockingQueue;
    
    private final RejectHandle rejectHandle;
    
    private final ThreadFactory threadFactory;
    
    public MyThreadPool (int corePoolSize, int maxSize, int timeout, TimeUnit timeUnit, BlockingQueue<Runnable> blockingQueue, RejectHandle rejectHandle, ThreadFactory threadFactory) {
        this.corePoolSize = corePoolSize;
        this.maxSize = maxSize;
        this.timeout = timeout;
        this.timeUnit = timeUnit;
        this.blockingQueue = blockingQueue;
        this.rejectHandle = rejectHandle;
        this.threadFactory = threadFactory;
    }
    
    
    List<Thread> coreList = new ArrayList<>();
    
    List<Thread> supportList = new ArrayList<>();
    
    void.execute(Runnable command) {
        if (coreList.size() < corePoolSize) {
            Thread thread = threadFactory.newThread(new CoreTask());
            coreList.add(thread);
            thread.start();
        }
        if (blockingQueue.offer(command)) {
            return;
        }
        if (coreList.size() + supportList.size() < maxSize) {
            Thread thread = threadFactory.newThread(new SupportThread());
            supportList.add(thread);
            thread.start();
        }
        if (!blockingQueue.offer(command)) {
            rejectHandle.reject(command, this);
        }
    }
    
    class CoreThread extends Thread {
        @Override
        public void run() {
            while (true) {
                try {
                    Runnable command = blockingQueue.take();
                    command.run();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
    
    class SupportThread extends Thread {
        @Override
        public void run() {
            while (true) {
                try {
                    Runnable command = blockingQueue.poll(timeout, timeUnit);
                    if (null == command) {
                        break;
                    }
                    command.run();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                };
                System.out.println(Thread.currentThread().getName() + "线程结束");
            }
        }
    }
    
}