JAVA------多线程之线程池

概述

是什么?

看名字线程池是在一个池(容器)里面事先创建若干个线程,需要的时候直接从池子里拿而不是自己创建,从而减少线程对象创建和销毁的时间开销。

为什么?

      今假设创建一线程执行某任务,执行时间如下:创建线程T1+执行任务T2+销毁线程T3,当T1+T3>T2时,那么当此种任务数量很多时,那么大多数的时间都是在创建和销毁线程,性能利用率低下。

此时使用线程池能极大地提高吞吐量,提高效率。这种情况可以类比仓库工具管理,假设仓库有10把扳手,当有工人需要扳手的时候直接向仓库申请,用完再还回来,而不用自己造扳手,还回来的扳手还可以借给其他工人使用,减少无效的工作时间。

有什么?

   ExcutorService是线程池的接口,而ThreadPoolExecutor是ExcutorService的默认实现,其内部结构示意如下:

 HashSet<Worker>:工作线程集合,需要线程的时候从这里拿;

 BlockingQueue<Runnable>:任务队列,当前集合中的工作线程都已工作,则新任务放入任务队列中等待。可以分为无界队列(LinkedBlockingQueue,有多少放多少)和有界队列(ArrayBlockingQueue,只能放有限的任务,超出则拒绝)。

使用示例

先看下线程池的简单的使用,代码如下:

public class T06_ThreadPool {
    public static void main(String[] args) {
        ExecutorService executorService = new ThreadPoolExecutor(1, 4, 1L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
        for (int i = 0; i <3 ; i++) {
            executorService.execute(()->{
                System.out.println(Thread.currentThread().getName() + " is  working");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            });

        }
     

       Future future=executorService.submit(new T());
        try {
            System.out.println(future.get());

        Future future1=executorService.submit(()->{
            System.out.println("Hello");
            return "Hello";
        });
        System.out.println(future1.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("End");
        executorService.shutdown();
    }
    static class T implements Callable<Integer>{

        @Override
        public Integer call() throws Exception {
            return 10086;
        }
    }
}

输出结果:

使用时分两步

  1. 创建线程池,设置线程池的各个参数;
  2. 向线程池传递任务,执行submit(Runnable) 或 execute(Callable)方法,(有返回值),提交任务;

源码分析

根据线程池的内部结构可以容易的想到当我们创建一个线程池时候,需要我们自己根据需求制定线程池的属性。首先就是“池子”的大小,线程池里面有多少个线程,那么这个就是corePoolSize,直译过来就是核心线程数,问题来了,为什么叫核心线程数,不直接叫线程数,那是因为线程池里还有“临时工”线程,就好比饭店在饭点时,编制内服务员忙不过来,又叫了几个童工充当临时服务员,过了饭点没什么人就叫人该干啥干啥去了,所以此时编制内和编外数量和就是maximumPoolSize。

既然有“临时工”线程,那么“临时工”线程在没事干的时候自然就会被销毁,那么此时就有一个属性keepAliveTime,超过这个时间没有“活”那就销毁,用unit指定单位。

线程池在使用的时候,调用execute方法传入Runable任务,若此时所有线程都被派出去干活了,包括临时工,那么此时总得要有个什么“东西”来存这些任务,而这个“东西”在这就是workQueue,一个阻塞队列就好比中午去海底捞吃火锅,里面没桌子了,只能找门口的小姐姐要个号,坐在门口玩手机,嗑瓜子等叫号。

阻塞队列又分为有界和无界的,而当使用有界队列时且队列中已经塞满了任务时,进来新的任务既无法执行,也没地儿等待,所以此时总要有个处理方案,而这就是handler,用于处理队列满时的新任务。

而ThreadPoolExecutor的构造方法如下:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 
结合上面的分析,各个参数意义如下:
  • corePoolSize:池中所保存的线程数,包括空闲线程。
  • maximumPoolSize:池中最大线程数。
  • keepAliveTime:临时线程等待任务时的保留最长时间。
  • unit:keepAliveTime的单位。
  • workQueue:执行前用于保持任务的队列。此队列仅保持由 execute方法提交的 Runnable任务。队列分为以下三种
  1. 有界队列:如ArrayBlockingQueue。

  2. 无界队列:如LinkedBlockingQueue,来多少任务都能够保存。

  3. 同步移交:如SynchronizeQueue,它将任务直接提交给线程而不保持它们。

  • threadFactory:创建新线程的线程工厂(如:Executors.defaultThreadFactory())。
  • handler:拒绝策略,当线程池已经shutdown,或者任务队列已满,此时调用拒绝策略。有以下四种拒绝策略:
  1. AbortPolicy:使用这种策略的线程池,将在无法继续接受新任务时,给任务提交方抛出RejectedExecutionException,让他们决定要如何处理;
  2. CallerRunsPolicy将把任务交给调用方所在的线程去执行;
  3. DiscardPolicy直接丢弃掉新来的任务;
  4. DiscardOldestPolicy丢弃最旧的一条任务,其实就是丢失blockingQueue.poll()返回的那条任务,要注意,如果你使用的是PriorityBlockingQueue优先级队列作为你的任务队列,那么这个策略将会丢弃优先级最高的任务,所以一般情况下,PriorityBlockingQueue和DiscardOldestPolicy不会同时使用;

submit(Runnable)源码如下:

​
public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
       
        int c = ctl.get();
        //当前工作线程小于核心线程数,则新建一个线程,并执行command任务
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //将线程加入等待队列
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            //双重检查当前状态,若此时线程池不处于运行状态,则从等待队列中移除任务(回滚队列),并执行拒绝策略
            if (! isRunning(recheck) && remove(command))
                reject(command);
          //此时无工作线程
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

​

   

java在Executors里面写了几个线程池的工厂方法提供的几种线程池:

newFixedThreadPool:固定大小的线程池,corePoolSize和maximumPoolSize相等的线程池。

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

newCachedThreadPool:缓存池,当有需要时才创建线程,执行完60s后回收。但是此线程池的最大线程数不可控。

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

 newSingleThreadExecutor:只有一个线程的线程池

public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));
    }

newScheduledThreadPool :定时任务的线程池

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

总结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值