线程池

线程池

  • 线程池参数介绍
    在这里插入图片描述
    1. corePoolSize:核心线程数:线程池在完成初始化时,在默认情况下,线程池并没有任何线程,线程池会在等待任务到来时再创建线程去执行。
    2. maxPoolSize:线程池有可能会在核心线程数的基础上,额外增加一些线程,但这些新增加的线程有一个上限,就是maxPoolSize。
    3. keepAliveTime:如果线程池当前线程多余corePoolSize,那么如果多余的线程空闲时间超过keepAliveTime,它们就会被终止。
    4. ThreadFactory:新的线程是由ThreadFactory创建的,默认使用Executors.defaultThreadFactory(),创建出来的线程都在同一个线程组,拥有相同的NORM_PRIORITY优先级并且都不是守护线程。如果自己指定ThreadFactory,那么就可以改变线程名、线程组、优先级、是否是守护线程等。通常我们使用默认的ThreadFactory就可以了。
  • 添加线程的规则
    1. 如果线程数小于corePoolSize,即使其他线程处于空闲状态,也会创建一个新的线程来执行任务。
    2. 如果线程数等于大于corePoolSize,但小于maxPoolSize,则将任务放入队列。
    3. 如果队列已满,但是线程数小于maxPoolSize,则创建一个新的线程来执行任务。
    4. 如果队列已满,并且线程数大于或等于maxPoolSize,则拒绝该任务。

在这里插入图片描述

  • 是否需要增加线程,判断顺序:corePoolSize -> workQueue -> maxPoolSize.
  • 增减线程的特点:
    1. 通过设置corePoolSize和maxPoolSize相同,就可以创建固定大小的线程池。
    2. 线程池希望保持较少的线程数,并且在负载变的很大时才增加它。
    3. 通过设置maxPoolSize为很高的值,如Integer.MAX_VALUE,可以允许线程池容纳任意数量的并发任务。
    4. 是在队列填满时才创建对于corePoolSize的线程,所以如果你使用的是无界队列(如LinkedBlockingQueue),那么线程数就不会超过corePoolSize。
  • 工作队列:有3种常见的工作队列
    1. 直接交接:SynchronousQueue
    2. 无界队列:LinkedBlockingQueue
    3. 有界队列:ArrayBlockingQueue
  • 线程池应该手动创建还是自动创建:
    1. 手动创建好,因为这样让我们可以更加明确线程池的运行规则,避免资源耗尽的风险。
    2. newFixedThreadPool:由于传进去的LinkedBlockingQueue是没有上限的,所以当请求数越来越多,并且无法及时处理完毕的时候,会容易造成占用大量的内存,可能会导致OOM。
    3. newSingleThreadExecutor:这个和newFixedThreadPool原理是一样的,只不过将线程数设置成了1,所以这会导致一样的问题,也就是当请求堆积的时候回占用大量的内存。
    4. newCachedThreadPool:弊端是这里得maxPoolSize被设置成了Integer.MAX_VALUE,这可能创建出非常多的线程,甚至导致OOM.
    5. newScheduledThreadPool:
  • FixedThreadPool:
    1. 固定数量的线程池
    2. 队列为LinkedBlockingQueue
  • CachedThreadPool
    1. 可缓存线程池
    2. 特点:无界线程池,具有自动回收多余线程的能力。
    3. 队列为SynchronousQueue

在这里插入图片描述

  • ScheduleThreadPool:
    1. 支持定时及周期性任务执行的线程池
    2. 对列为DelayedWorkQueue
  • SingleThreadExecutor:
    1. 单线程的线程池,它只会用唯一的工作线程来执行任务。
    2. 它的原理和FixedThreadPool是一样的,但是此时线程数被设置为了1。
    3. 队列为LinkedBlockingQueue

在这里插入图片描述

  • workStealingPool:
    1. 这个线程池和之前的有很大的不同。
    2. 子任务
    3. 窃取

线程池代码演示

public class ThreadPool {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(4);
//        ExecutorService executorService = Executors.newSingleThreadExecutor();
//        ExecutorService executorService = Executors.newCachedThreadPool();
//        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
//        scheduledExecutorService.schedule(new Task(),1, TimeUnit.SECONDS);
//        scheduledExecutorService.scheduleAtFixedRate(new Task(),1,3,TimeUnit.SECONDS);
//        for(int i=0;i<1000;i++){
//            executorService.execute(new Task());
//        }

        for(int i=0;i<1000;i++){
            executorService.execute(new ShutDowmTask());
        }
        Thread.sleep(1500);
//        System.out.println(executorService.isShutdown());
//        executorService.shutdown();
//        executorService.execute(new ShutDowmTask());

//        System.out.println(executorService.isShutdown());
//        System.out.println(executorService.isTerminated());
//        Thread.sleep(15000);
//        System.out.println(executorService.isTerminated());

//        executorService.shutdown();
//        boolean b = executorService.awaitTermination(3l,TimeUnit.SECONDS);
//        System.out.println(b);

        //runnables 为已经放在队列中但是还没哟执行的任务。
        List<Runnable> runnables = executorService.shutdownNow();
    }
}
class Task implements Runnable{

    @Override
    public void run() {
        try {
            Thread.sleep(500);
            System.out.println(Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class ShutDowmTask implements Runnable{

    @Override
    public void run() {
        try {
            Thread.sleep(500);
            System.out.println(Thread.currentThread().getName());
        } catch (InterruptedException e) {
//            e.printStackTrace();
            System.out.println(Thread.currentThread().getName()+"被中断里");
        }
    }
}
  • 线程池里的线程数量设多少比较合适:

    1. CPU密集型(加密、计算hash等 ):最佳线程数为核心数的1-2倍。
    2. 耗时IO型(读取数据库、文件、网络读取时):最佳线程数一般会大于CPU核心数的很多倍,以JVM线程监控显示繁忙情况为依据,保证线程空闲可衔接上。
    3. 线程数=CPU核心数*(1+平均等待时间/平均工作时间)
  • 停止一个线程

    1. shutdown
    2. isShutdown
    3. isTerminated
    4. awaitTermination
    5. shutdownNow
  • 线程池拒绝任务:

    1. 当Executor关闭时,提交新任务会被拒绝。
    2. 当Executor对最大线程和工作队列容量有限边界并且已经饱和时。
      在这里插入图片描述
  • 拒绝策略:

    1. AbortPolicy:直接抛出个RejectedExecutionException异常,也不执行这个任务了
    2. DiscardPolicy:采用这个拒绝策略,会让被线程池拒绝的任务直接抛弃,不会抛异常也不会执行。
    3. DiscardOldestPolicy:当任务呗拒绝添加时,会抛弃任务队列中最旧的任务也就是最先加入队列的,再把这个新任务添加进去。
    4. CallerRunsPolicy:在任务被拒绝添加后,会调用当前线程池的所在的线程去执行被拒绝的任务。缺点就是可能会阻塞主线程。
  • Executor家族:

  • 线程池、ThreadPoolExecutor、ExecutorService、Executor、Executors
    在这里插入图片描述

  • 线程池的状态

    1. RUNNING:接受新任务并处理排队任务。
    2. SHUTDOWN:不接受新任务,但处理排队任务 。
    3. STOP:不接受新任务,也不处理排队任务,并中断正在进行的任务。
    4. TIDYING:中文是整洁,指所有的任务都已终止,workCount为零时,线程会转换到TIDYING状态,并将运行terminate()钩子方法。
    5. TERMINATED:terminate()方法运行结束。

在这里插入图片描述

  • 使用线程池注意点:
    1. 避免任务堆积
    2. 避免线程数过多增加
    3. 排查线程泄漏

自定义一个线程池

/**
 * 自定义一个线程池 演示每个线程任务执行前后方钩子函数
 */
public class PauseableThreadPool extends ThreadPoolExecutor {

    private boolean isPaused;
    private final ReentrantLock lock = new ReentrantLock();
    private Condition unpaused = lock.newCondition();
    public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
    }

    public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
    }

    public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
    }

    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);
        lock.lock();

            try {
                while (isPaused) {
                    unpaused.await();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
    }

    private void pause(){
        lock.lock();
        try{
            isPaused = true;
        }finally {
            lock.unlock();
        }
    }

    public void resume(){
        lock.lock();
        try{
            isPaused = false;
            unpaused.signalAll();
        }finally {
            lock.unlock();
        }

    }

    public static void main(String[] args) throws InterruptedException {
        PauseableThreadPool pauseableThreadPool = new PauseableThreadPool(10, 20, 10l, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
        Runnable runnable = new Runnable() {

            @Override
            public void run() {
                System.out.println("我被执行");
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        for(int i =0;i<10000;i++){
            pauseableThreadPool.execute(runnable);
        }

        Thread.sleep(1500);
        pauseableThreadPool.pause();
        System.out.println("线程池被暂停了");
        Thread.sleep(1500);
        pauseableThreadPool.resume();
        System.out.println("线程池被恢复了");
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值