美文网首页
Java线程池全面解析

Java线程池全面解析

作者: zzc不是自助餐 | 来源:发表于2019-03-13 13:36 被阅读0次

    开篇:为什么要使用线程池?

    在回答这个问题之前我们先想想为什么要使用多线程?
    answer:由于计算机处理器的处理速度极大的超过了计算机存储和通信子系统的处理速度,在单线程情况下当进行磁盘IO操作或者进行网络请求时会导致处理器大部分时间处于空闲状态,这就造成了处理器资源的浪费。同时Android中规定主线程只能进行UI操作,对于耗时操作则需要放到子线程,否则可能会导致ANR。所以为了尽量的“压榨”处理器的效率,使得计算机同时能够处理多个任务,提高任务执行效率,Java引入了多线程。

    那为什么要使用线程池呢?
    answer:对于每个jvm都有限定的内存大小,若无限制的去创建新线程轻者导致虚拟机频繁的GC造成卡顿,重者直接oom导致虚拟机崩溃。并且计算机处理器的并发处理数量也是有限的,超过一定数量的线程并不会加快处理器处理的效率。所以为了维护Java虚拟机环境的绿色稳定,充分利用处理器的速度,统一管理线程,对线程按照一定规则进行调度处理,Java引入了线程池。

    so:如何去创建线程池?

    我们可以直接使用ThreadPoolExecutor对象来创建线程池对象,也可以通过Java提供的四种线程类来快速的创建线程池对象。

    代码:通过ThreadPoolExecutor创建线程池

    ExecutorService service = new ThreadPoolExecutor(3,3,
                    10, TimeUnit.SECONDS,new LinkedBlockingDeque<Runnable>());
    
    /**
         * Creates a new {@code ThreadPoolExecutor} with the given initial
         * parameters and default thread factory.
         *
         * @param corePoolSize the number of threads to keep in the pool, even
         *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
         * @param maximumPoolSize the maximum number of threads to allow in the
         *        pool
         * @param keepAliveTime when the number of threads is greater than
         *        the core, this is the maximum time that excess idle threads
         *        will wait for new tasks before terminating.
         * @param unit the time unit for the {@code keepAliveTime} argument
         * @param workQueue the queue to use for holding tasks before they are
         *        executed.  This queue will hold only the {@code Runnable}
         *        tasks submitted by the {@code execute} method.
         * @param handler the handler to use when execution is blocked
         *        because the thread bounds and queue capacities are reached
         * @throws IllegalArgumentException if one of the following holds:<br>
         *         {@code corePoolSize < 0}<br>
         *         {@code keepAliveTime < 0}<br>
         *         {@code maximumPoolSize <= 0}<br>
         *         {@code maximumPoolSize < corePoolSize}
         * @throws NullPointerException if {@code workQueue}
         *         or {@code handler} is null
         */
        public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue,
                                  RejectedExecutionHandler handler) {
            this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
                 Executors.defaultThreadFactory(), handler);
        }
    

    corePoolSize:核心线程数,默认一直存在于线程池中,除非allowCoreThreadTimeOut设置为true,则核心线程也会在空闲keepAliveTime之后回收。
    maximumPoolSize:最大线程数,包含核心线程和非核心线程,非核心线程在空闲keepAliveTime之后会回收。
    keepAliveTime:空闲线程存活时间。
    unit:时间单位(时分秒啥的)。
    workQueue:工作队列,所有需要执行的Runnable对象都会被加入到此队列中。
    threadFactory: 创建线程的工厂类,默认DefaultThreadFactory。
    handler:RejectedExecutionHandler接口对象,默认AbortPolicy,当队列达到限额时会阻塞然后执行RejectedExecutionHandler中的rejectedExecution方法。

    代码:通过Executors的静态方法创建线程池对象。
    通过上述对ThreadPoolExecutor构造器的分析,我们就能很好的理解下面四种线程池的特点。
    1、FixedThreadPool,只有核心线程的线程池,空闲状态下默认不回收。代码如下图。

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

    2、CachedThreadPool,只有非核心线程的线程池,线程空闲超过60秒将会被回收。代码如下图。

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

    3、ScheduledThreadPool,既有核心线程(由corePoolSize指定)又有非核心线程的线程池,非核心线程数的最大值为Integer.MAX_VALUE。代码如下图。

    ExecutorService service = Executors.newScheduledThreadPool(3);
    
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
     }
    

    4、SingleThreadExecutor,只有一个核心线程的线程池。代码如下。

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

    最后:使用线程池对象去执行线程方法。

    通过ExecutorService提供的execute、submit方法,将线程添加到线程池中进行执行处理。

    代码:通过execute(Runnable runnable)往线程池添加Runnable对象

    service.execute(new Runnable() {
          @Override
          public void run() {
                 System.out.println("我在子线程执行,呵呵");
          }
    );
    

    代码:通过submit(Callable<T> callable)往线程池添加Callable对象

    Future<Integer> task = service.submit(new Callable<Integer>() {
          @Override
          public Integer call() throws Exception {
              System.out.println("我也在子线程执行,呵呵");
              return 66;
          }
    });
    //将阻塞当前线程直到task获取到返回值66
    task.get();
    

    代码:通过submit(Runnable runnable)往线程池添加Runnable对象

    Future task1 = service.submit(new Runnable() {
          @Override
          public void run() {
              System.out.println("我也在子线程执行,呵呵");
          }
    });
            
    //将阻塞当前线程直到task获取到返回值null
    ask1.get();
    

    代码:通过submit(Runnable runnable, T result)往线程池添加Runnable对象并指定返回值result

    Future<String> task2 = service.submit(new Runnable() {
        @Override
        public void run() {
            System.out.println("我也在子线程执行,呵呵");
        }
      }, "hello");
    
     //将阻塞当前线程直到task获取到返回值hello
     task2.get();
    

    这样我们就可以创建和使用线程池了,写得不好也点个赞吧,呵呵。

    相关文章

      网友评论

          本文标题:Java线程池全面解析

          本文链接:https://www.haomeiwen.com/subject/pyhzpqtx.html