线程池

继承Thread的弊端

1.每次new Thread的时候都需要新建一个线程,性能差

2.线程缺乏统一管理,可能无限制的新建线程,相互竞争,有可能占用过多系统资源导致死机或者OOM

3.Thread类缺少更多功能,比如更多的执行、定期执行、线程中断。

线程池的好处

1.重用存在的线程,减少对象创建、消亡的开销、性能佳

2.可以有效的控制最大并发线程数,提高系统资源利用率,同时可以避免过多资源竞争,避免阻塞。

3.提供定时执行、定期执行、单线程、并发数控制等功能。

 

线程池-ThreadPoolExecutor

参数说明:ThreadPoolExecutor一共有七个参数,这七个参数配合起来,构成了线程池强大的功能。

corePoolSize:核心线程数线程池的基本大小,即在没有任务需要执行的时候线程池的大小,并且只有在工作队列满了的情况下才会创建超出这个数量的线程。这里需要注意的是:在刚刚创建ThreadPoolExecutor的时候,线程并不会立即启动,而是要等到有任务提交时才会启动,除非调用了prestartCoreThread/prestartAllCoreThreads事先启动核心线程。

maximumPoolSize:线程最大线程数,线程池中允许的最大线程数,线程池中的当前线程数目不会超过该值。如果队列中任务已满,并且当前线程个数小于maximumPoolSize,那么会创建新的线程来执行任务。

workQueue : 阻塞队列,存储等待执行的任务,很重要,会对线程池运行过程产生重大影响

ThreadPoolExecutorexecute方法分下面4种情况。
1)如果当前运行的线程少于corePoolSize则创建新线程来行任(注意,一步骤需要取全局)。
2)如果运行的线程等于或多于corePoolSize将任加入BlockingQueue
3)如果无法将任加入BlockingQueue列已),则创建新的线程来理任(注意,执行一步需要取全局)。
4)如果建新线程将使当前运行的线程超出maximumPoolSize,任将被拒,并用RejectedExecutionHandler.rejectedExecution()方法。

线程池大小调节策略。通过corePoolSize和maximumPoolSize,控制如何新增线程;通过allowCoreThreadTimeOut和keepAliveTime,控制如何销毁线程。

allowCoreThreadTimeOut:

该属性用来控制是否允许核心线程超时退出。如果线程池的大小已经达到了corePoolSize,不管有没有任务需要执行,线程池都会保证这些核心线程处于存活状态。可以知道:该属性只是用来控制核心线程的。

keepAliveTime:

线程超时退出时间。举个例子,如果线程池的核心大小corePoolSize=5,而当前大小poolSize =8,那么超出核心大小的线程,会按照keepAliveTime的值判断是否会超时退出。如果线程池的核心大小corePoolSize=5,而当前大小poolSize =5,那么线程池中所有线程都是核心线程,这个时候线程是否会退出,取决于allowCoreThreadTimeOut。

unit : keepAliveTime的时间单位

threadFactory:线程工厂,用来创建线程

rejectHandler:当拒绝处理任务时的策略

四种拒绝策略:

AbortPolicy :直接抛出异常

CallerRunsPolicy   :用调用者所在的线程来执行任务

DiscardOldestPolicy :线程池会放弃等待队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中。 

DiscardPolicy    :不处理,直接丢弃

 

  • running:能接受新提交的任务,也能处理阻塞队列中的任务
  • shutdown:不能处理新的任务,但是能继续处理阻塞队列中任务
  • stop:不能接收新的任务,也不处理队列中的任务
  • tidying:如果所有的任务都已经终止了,这时有效线程数为0
  • terminated:最终状态
序号方法名描述
1 execute() 提交任务,交给线程池执行
2 submit() 提交任务,能够返回执行结果 execute+Future
3 shutdown() 关闭线程池,等待任务都执行完
4 shutdownNow() 关闭线程池,不等待任务执行完
5 getTaskCount() 线程池已执行和未执行的任务总数
6 getCompleteTaskCount() 已完成的任务数量
7 getPoolSize() 线程池当前的线程数量
8 getActiveCount()

当前线程池中正在执行任务的线程数量

使用Executor创建线程池

使用Executor可以创建四种线程池:分别对应上边提到的四种线程池初始化方法

1、Executors.newCachedThreadPool 

  • 它是一个可以无限扩大的线程池;
  • 它比较适合处理执行时间比较小的任务;
  • corePoolSize为0,maximumPoolSize为无限大,意味着线程数量可以无限大;
  • keepAliveTime为60S,意味着线程空闲时间超过60S就会被杀死;
  • 采用SynchronousQueue装等待的任务,这个阻塞队列没有存储空间,这意味着只要有请求到来,就必须要找到一条工作线程处理他,如果当前没有空闲的线程,那么就会再创建一条新的线程。
//使用方法:
public static void main(String[] args) {
    ExecutorService executorService = Executors.newCachedThreadPool();
    for (int i = 0; i < 10; i++) {
        final int index = i;
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                log.info("task:{}", index);
            }
        });
    }
    executorService.shutdown();
}

 

值得注意的一点是,newCachedThreadPool的返回值是ExecutorService类型,该类型只包含基础的线程池方法,但却不包含线程监控相关方法,因此在使用返回值为ExecutorService的线程池类型创建新线程时要考虑到具体情况。 

2、newFixedThreadPool 
定长线程池,可以线程现成的最大并发数,超出在队列等待

  • 它是一种固定大小的线程池;
  • corePoolSize和maximunPoolSize都为用户设定的线程数量nThreads;
  • keepAliveTime为0,意味着一旦有多余的空闲线程,就会被立即停止掉;但这里keepAliveTime无效;
  • 阻塞队列采用了LinkedBlockingQueue,它是一个无界队列;
  • 由于阻塞队列是一个无界队列,因此永远不可能拒绝任务;
  • 由于采用了无界队列,实际线程数量将永远维持在nThreads,因此maximumPoolSize和keepAliveTime将无效。
//使用方法:
public static void main(String[] args) {
    ExecutorService executorService = Executors.newFixedThreadPool(3);
    for (int i = 0; i < 10; i++) {
        final int index = i;
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                log.info("task:{}", index);
            }
        });
    }
    executorService.shutdown();
}

3、newSingleThreadExecutor 
单线程化的线程池,用唯一的一个共用线程执行任务,保证所有任务按指定顺序执行(FIFO、优先级…)

  • 它只会创建一条工作线程处理任务;
  • 采用的阻塞队列为LinkedBlockingQueue;
//使用方法:
public static void main(String[] args) {
    ExecutorService executorService = Executors.newSingleThreadExecutor();
    for (int i = 0; i < 10; i++) {
        final int index = i;
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                log.info("task:{}", index);
            }
        });
    }
    executorService.shutdown();
}

4、newScheduledThreadPool 
定时线程池,支持定时和周期任务执行

//基础使用方法:
public static void main(String[] args) {
    ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
    executorService.schedule(new Runnable() {
        @Override
        public void run() {
            log.warn("schedule run");
        }
    }, 3, TimeUnit.SECONDS);//延迟3秒执行
    executorService.shutdown();
}

小扩展:延迟执行任务的操作,java中还有Timer类同样可以实现

Timer timer = new Timer();
timer.schedule(new TimerTask() {
    @Override
    public void run() {
        log.warn("timer run");
    }
}, new Date(), 5 * 1000);

 

posted @ 2018-07-07 09:37  开拖拉机的蜡笔小新  阅读(371)  评论(0编辑  收藏  举报