Executor 线程池

1、什么是线程池:  java.util.concurrent.Executors提供了一个 java.util.concurrent.Executor接口的实现用于创建线程池

多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。    
    假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间。

   如果:T1 + T3 远大于 T2,则可以采用线程池,以提高服务器性能。

     一个线程池包括以下四个基本组成部分:
                1、线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
                2、工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
                3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
                4、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。

   线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提高服务器程序性能的。它把T1,T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1,T3的开销了。
    线程池不仅调整T1,T3产生的时间段,而且它还显著减少了创建线程的数目,看一个例子:
    假设一个服务器一天要处理50000个请求,并且每个请求需要一个单独的线程完成。在线程池中,线程数一般是固定的,所以产生线程总数不会超过线程池中线程的数目,而如果服务器不利用线程池来处理这些请求则线程总数为50000。一般线程池大小是远小于50000。所以利用线程池的服务器程序不会为了创建50000而在处理请求时浪费时间,从而提高效率。

线程池的作用:

线程池作用就是限制系统中执行线程的数量。
     根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程排队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。

为什么要用线程池:

1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。

2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService

线程池介绍

package com.cmy.Executor.demo1;
 
 
import java.util.concurrent.*;
 
/**
 * 线程复用:线程池(为了避免系统频繁的创建和销毁线程,我们可以将创建的线程进行复用)
 * JDK对线程池的支持:
 *       JDK提供的Executors框架,可以让我们有效的管理和控制我们的线程,其本质上就是一个线程池
 *       Executor接口的继承关系如下所示:
 *                       Executor
 *                          ↑
 *                    ExecutorService
 *                ↑                        ↑
 *     AbstractExecutorService    ScheduledExecutorService
 *                ↑                        ↑
 *      ThreadPoolExecutor                 ↑
 *                ↑                        ↑
 *                 ScheduledThreadPoolExecutor
 *
 *
 *    Executors类常用方法如下:
 *
 *    1、newFixedThreadPool:该方法返回一个固定线程数量的线程池;
 *
 *    2、newSingleThreadExecutor:该方法返回一个只有一个现成的线程池;
 *
 *    3、newCachedThreadPool:返回一个可以根据实际情况调整线程数量的线程池;
 *
 *    4、newSingleThreadScheduledExecutor:该方法和newSingleThreadExecutor的区别是给定了时间执行某任务的功能,可以进行定时执行等;
 *
 *    5、newScheduledThreadPool:在4的基础上可以指定线程数量。
 *
 *
 *    查看Executors类的源码如下: 例如newFixedThreadPool (同学们可自主查看)
 *
 *    以下为:newFixedThreadPool的实现:
 *            public static ExecutorService newFixedThreadPool(int nThreads) {
 *                   return new ThreadPoolExecutor(nThreads, nThreads,
 *                      0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
 *            }
 *
 *    本质上是创建了ThreadPoolExecutor的实例而已!
 *
 *    ThreadPoolExecutor构造参数如下:
 *        public ThreadPoolExecutor(int corePoolSize,
             int maximumPoolSize, 核心线程池大小
             long keepAliveTime,  线程最大容量
             TimeUnit unit,       空闲时,线程的存活时间
             ThreadFactory,       线程工厂
             BlockingQueue<Runnable> workQueue, 任务队列
             RejectedExecutionHandler handler) {  线程的拒绝策略
                       this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
                             Executors.defaultThreadFactory(), handler);
          }
 *
 *
 *
 */
public class ThreadPoolDemo {
    public static void main(String[] args){
         /*在此 根据alibaba编码规范的提示
 
             ‘不推荐使用Executors中的方法创建线程池 而是推荐创建ThreadPoolExecutor实例的方式’
             以下为alibaba介绍:
 
               线程池不允许使用Executors去创建,而是使用ThreadPoolExecutor的方式,这样做可以规避很多风险
 
               说明:各个方法的弊端
 
               newFixedThreadPool和newSingleThreadExecutor:
                  主要问题是堆积的请求处理队列可能会耗费非常大的内存
 
               newCachedThreadPool和newScheduledThreadPool
                  主要问题是线程最大数Integer.MAX_VALUE 可能会创建过多的线程 导致资源浪费
 
          */
 
         //不推荐做法:ExecutorService service=Executors.newFixedThreadPool(4); //获取拥有4条线程的线程池
 
         //推荐做法
         ExecutorService service=new ThreadPoolExecutor(2,
                                                        2,
                                                        0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>(10),Executors.defaultThreadFactory());
 
         for (int i=0;i<10;i++){
             int index=i;
             //使用线程池中的线程重复打印index的值
             service.submit(()->
                 System.out.println(Thread.currentThread().getName()+" i:"+index+"service"));
         }
 
         service.shutdown();//线程停止运行
    }
}
 
 
 
线程池的使用
package com.cmy.Executor.demo2;
 
//使用submit函数带来的问题
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
 
public class ThreadPoolDemo {
    public static void main(String[] args){
        ExecutorService service= Executors.newFixedThreadPool(4);
 
        for(int i=0;i<5;i++){
            int index=i;
            //此处index的初值为0 但控制台上不会输出任何异常(原因 内部捕获了异常)
            //service.submit(()-> Calc(100,index));
 
            //1:替换为execute
            //service.execute(()->Calc(100,index));
 
            //2:或者使用Future模式 自定义异常捕获和信息
            Future future=service.submit(()->Calc(100,index));
 
            try {
                future.get();
            } catch (Exception e) {
                System.out.println("我是异常信息");
            }
        }
 
        service.shutdown();
    }
 
    public static void Calc(int a,int b){
          double c=a/b;
          System.out.println(c);
    }
}

猜你喜欢

转载自www.cnblogs.com/zhu12345/p/9760309.html