Java 线程池

java线程池

ThreadPoolExecutor

  • 初始只会创建corePoolSize个线程
  • 扩容线程数的条件是,任务队列满了,如果queue很大,那么只会用核心线程池
  • 超过核心线程数的线程,如果现超过最大活跃时间没有被使用,就会被回收
  • 缺点:在queue没满以前只会使用核心线程数,queue满了以后创建新的线程取走一个任务,如果这时候一个任务执行很长,或者任务突然来了很多,把最大线程数也用完了,就会启用拒绝策略
    public void ThreadPoolExecutor() throws InterruptedException {
        LinkedBlockingQueue blockingQueue = new LinkedBlockingQueue(10);
        ThreadFactory simpleThreadFactory = new SimpleThreadFactory();
        DelayedAdditionRejectedExecutionHandler executionHandler = new DelayedAdditionRejectedExecutionHandler();

        ThreadPoolExecutor  threadPoolExecutor = new ThreadPoolExecutor(1,10,3, TimeUnit.SECONDS,blockingQueue, simpleThreadFactory,executionHandler);
        for(;;){
            threadPoolExecutor.getRejectedExecutionHandler();
            threadPoolExecutor.execute(()->{
                System.out.println( Thread.currentThread().getName() +"开始" );
                try {
                    Thread.sleep(5000L);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println( Thread.currentThread().getName() +"结束" );
            });
            Thread.sleep(1000L);
        }

    }

ThreadPoolExecutor 的继承体系

  • 继承关系 ThreadPoolExecutor -->AbstractExecutorService -->ExecutorService ->Executor
  • ThreadPoolExecutor 对象,这个对象的 AbstractExecutorService 的 子类 ,AbstractExecutorService 是 ExecutorService 的 抽象类。
  • 如果使用Executors.newxxx创建线程池,返回的都是ExecutorService 的子类。

ThreadPoolExecutor 的参数

  • corePoolSize

    核心线程数,初始化的是有就有这么多个线程
    
  • maximumPoolSize

    最大的线程数,在workQueue满了的情况开始增加任务处理能力,类似临时工
    
  • keepAliveTime

    临时工线程,存活多久没有任务会被清除
    
  • TimeUnit

    keepAliveTime 时间单位
    
  • workQueue

    待处理的任务队列,也就是需要处理的任务缓冲区,当这个缓冲区满了以后,就会启用临时线程(maximumPoolSize ),队列的数量决定缓存区的大小如果缓冲区无限大,那么maximumPoolSize 就没有使用的机会了
    
  • threadFactory

    控制线程的创建,里面只有一个方法 newThread,实现它可以控制线程怎么产生
    
  • RejectedExecutionHandler

    当任务缓冲队列满了以后应该怎么处理,也就是拒绝策略,比如丢弃新的任务,丢弃老的任务,或者丢弃任务的时候抛出异常,或者使用主线程执行新的任务
    

RejectedExecutionHandler

当任务workQueue满了以后应该怎么处理,也就是拒绝策略,比如丢弃新的任务,丢弃老的任务,或者丢弃任务的时候抛出异常,或者使用主线程执行新的任务

  • AbortPolicy 抛出异常,这是大多数线程池的默认选择
    image-20250927223202045

  • DiscardPolicy 丢弃当前任务,实际上就是啥都没干,无视了任务满了和传递过来的任务

    image-20250927223254921

  • DiscardOldestPolicy 把队列中最前面的一个扔掉,然后把当前的任务放进去
    image-20250927223407850

  • CallerRunsPolicy 用当前线程来执行这个需要线程池执行的任务,这个一般不建议使用,因为会阻塞加入任务的主线程

    image-20250927223706864

我们可以通过实现RejectedExecutionHandler来实现自己的策略
比如等待一会再尝试把任务放回去,
image-20250927223908398

java提供的方便创建线程池的方法

image-20250927225515061

newSingleThreadExecutor

  • newSingleThreadExecutor ,创建的是一个核心线程和最大线程数都是1的线程池,但是阻塞队列是几乎无穷大(Integer.MAX_VALUE)
  • 也就是说,永远只有一个线程在执行任务,但是任务缓存队列是无限大的,所以几乎不会装满(装满的条件是阻塞队列满课,并且不能通过创建新的线程来减少满了的任务**)

    @Test
    public void newSingleThreadExecutor() throws InterruptedException {
        ExecutorService threadPoolExecutor = Executors.newSingleThreadExecutor(new SimpleThreadFactory());
        for(;;){
            threadPoolExecutor.execute(()->{
                System.out.println( Thread.currentThread().getName() +"开始" );
                try {
                    Thread.sleep(5000L);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println( Thread.currentThread().getName() +"结束" );
            });

            //只睡一毫秒无线添加任务,这样会创建最多5000个线程,程序不会卡死,如果不限制,如果不限制会创建无限对线程,程序卡死
            Thread.sleep(1L);
        }

    }

newFixedThreadPool

  • newFixedThreadPool**和 newSingleThreadExecutor 的区别就在线程的数量,别的都一样
    @Test
    public void newFixedThreadPool() throws InterruptedException {
        ExecutorService threadPoolExecutor = Executors.newFixedThreadPool(10,new SimpleThreadFactory());
        for(;;){
            threadPoolExecutor.execute(()->{
                System.out.println( Thread.currentThread().getName() +"开始" );
                try {
                    Thread.sleep(5000L);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println( Thread.currentThread().getName() +"结束" );
            });

            //只睡一毫秒无线添加任务,这样会创建最多5000个线程,程序不会卡死,如果不限制,如果不限制会创建无限对线程,程序卡死
            Thread.sleep(1L);
        }

    }

newCachedThreadPool

  • newCachedThreadPool 是一个核心线程数为0,最大线程数无限,阻塞队列是1的线程池。
  • 也就是说这个线程池任务永远不会满,阻塞队列是1,一个任务就满了,但是这时候会启动新的线程来取走这个任务,所以永远不会满
  • 优点:可以充分利用cpu资源
  • 缺点:没有对线程数量加以约束,容易滥用cpu资源,如果线程数量太多,远超**cpu 数量,那么上下文切换的耗时比执行任务的时间还长。
    @Test
    public void newCachedThreadPool() throws InterruptedException {
        ExecutorService threadPoolExecutor = Executors.newCachedThreadPool(new SimpleThreadFactory());
        for(;;){
            threadPoolExecutor.execute(()->{
                System.out.println( Thread.currentThread().getName() +"开始" );
                try {
                    Thread.sleep(5000L);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println( Thread.currentThread().getName() +"结束" );
            });

            //只睡一毫秒无线添加任务,这样会创建最多5000个线程,程序不会卡死,如果不限制,如果不限制会创建无限对线程,程序卡死
            Thread.sleep(1L);
        }

newScheduledThreadPool

  • 循环任务线程池
  • 里面的任务都会被循环执行
    public void newScheduledThreadPool() throws InterruptedException {
        ScheduledExecutorService threadPoolExecutor = Executors.newScheduledThreadPool(10,new SimpleThreadFactory());
        for(;;){

            //执行下一次任务的时间是10面以后,或者当前任务执行完成以后(当前任务的执行时间大于间隔时间的时候)
            threadPoolExecutor.scheduleAtFixedRate(()->{
                System.out.println( Thread.currentThread().getName() +"开始" );
                try {
                    Thread.sleep(5000L);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println( Thread.currentThread().getName() +"结束" );
            },3,10,TimeUnit.SECONDS);


            //每当上次任务执行完毕后,间隔10秒在执行下一次任务
            threadPoolExecutor.scheduleWithFixedDelay(()->{
                System.out.println( Thread.currentThread().getName() +"开始" );
                try {
                    Thread.sleep(5000L);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println( Thread.currentThread().getName() +"结束" );
            },3,10,TimeUnit.SECONDS);


            //只睡一毫秒无线添加任务,这样会创建最多5000个线程,程序不会卡死,如果不限制,如果不限制会创建无限对线程,程序卡死
            Thread.sleep(1L);
        }

    }

newWorkStealingPool

不指定并行数就是cpu核心数

  • 这个线程池想解决核心线程数被用完,并且queue满了.才会创建新线程的问题
  • 效果类似:核心线程是0,最大线程是执行的并发数,创建新线程的条件是queue不是空(而不是queue满了),执行完以后如果没有任务线程会马上回收,回收到只有一半的时候就不会回收了。
  • 虽然任务队列不再是一个,而是每一个线程一个,并且线程任务队列做完会执行别的线程的任务队列,其实他们还是共用的,只是不同线程与先分配一下,如果需要执行的任务小于线程数都会被立即执行,而不是任务队列满了才启动新的线程。
    @Test
    public void newWorkStealingPool() throws InterruptedException {
        ForkJoinPool threadPoolExecutor = (ForkJoinPool) Executors.newWorkStealingPool(10);
        for(int i = 0;;i++){
            //执行下一次任务的时间是10面以后,或者当前任务执行完成以后(当前任务的执行时间大于间隔时间的时候)
            System.out.println("getPoolSize:" + threadPoolExecutor.getPoolSize());;
            System.out.println("getParallelism:" + threadPoolExecutor.getParallelism());
            threadPoolExecutor.execute(()->{
                System.out.println( Thread.currentThread().getName() +"开始" );
                try {
                    Thread.sleep(5000L);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println( Thread.currentThread().getName() +"结束" );
            });

            System.out.println(i);
            //只睡一毫秒无线添加任务,这样会创建最多5000个线程,程序不会卡死,如果不限制,如果不限制会创建无限对线程,程序卡死

            if(i>10){
                Thread.sleep(10000L);
            }else{
                Thread.sleep(1000L);
            }


        }

posted on 2018-07-09 16:18  zhangyukun  阅读(142)  评论(0)    收藏  举报

导航