线程池Executor原理及生命周期

无限制创建线程的不足

1.线程生命周期的开销非常高。线程的创建并不是没有代价的。根据平台的不同,实际的开销也有所不同,但是线程的创建过程都会需要时间,延迟处理的请求,并且需要JVM和操作系统提供一些辅导操作。如果请求的到达率非常高且请求的处理过程是轻量级的,例如大多数服务器应用程序就是这种情况,那么为每个请求创建一个新线程将消耗大量的计算资源。

JVM线程使用的是1:1的线程模型,而内核线程的调用必须让CPU转为内核态,比普通调用更加浪费时间 

2.资源消耗。活跃的线程会消耗资源,尤其是内存。如果可运行的线程数量多于可用处理器的数量,那么有些线程将会闲置。大量的空闲的线程会占用许多内存,给垃圾回收器带来压力,而且大量线程在竞争CPU资源时还将产生其他的性能开销。如果你已经拥有足够多的线程使所有cpu保持忙碌状态,那么再创建更多的线程反而会降低性能。

CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再次加载这个任务的状态,从任务保存到再加载的过程就是一次上下文切换。

3.稳定性。在可创建线程的数量上存在一个限制。这个限制将随着平台的不同而不同,并且受多个因素制约,包括jvm的启动参数、Thread构造函数中请求的栈大小,以及底层操作的限制等。如果破坏这些限制,那么很可能抛出OutOfMemoryError异常,要想从这种错误中恢复过来是非常危险的,更简单的办法是通过构造程序来避免超出这种限制。

-Xms  最小堆内存  -Xmx  最大堆内存 
-Xss 设置每个线程的堆栈大小(JAVA线程栈占用的内存是堆外的,不是 Xmx里面的)。JDK5.0以后每个线程堆栈大小为1M
操作系统限制 系统最大可开线程数

总内存 = JAVA内存Xmx+ 其它程序内存P+空闲内存Free
还可开线程数= free/Xss+已开线程数<系统最大可开线程数?free/Xss:系统最大可开线程数-已开线程数

由以上问题看出,无限创建线程的危害,在jdk5以后提供Executor框架有效的为使用者提供了一个可用的方案。

 

Executor框架

public interface Executor {
    void execute(Runnable command);
}

       虽然Executor是一个简单的接口,但它却为灵活且强大的异步任务执行框架提供了基础,该框架能够支持多种不同类型的任务执行策略,它提供了一种标准的方法将任务的提交过程与执行过程解耦开来

       Executor基于生产者-消费者模式

           提交任务的线程(new Runnable)相当于生产者

           执行任务的线程  (ThreadPoolExecutor里的线程)则相当于消费者。

即然是生产者-消费者模式,因此ThreadPoolExecutor的设计必然要考虑下

  1. 消费者数量控制
  2. 缓存队列设计,如果满了怎么处理
  3. 任务的调度策略(FIFO,LIFO,优先级)
ThreadPoolExecutor的构造参数:

corePoolSize:核心线程池的poolSize。
maximumPoolSize:线程池最大大小
workQueue:等待队列。
keepAliveTime:当maximumPoolSize>corePoolSize且没有任务了,maximumPoolSize-corePoolSize的线程将在keepAliveTime时间后被销毁
unit:keepAliveTime 的时间单位 
threadFactory:是构造Thread的方法,你可以自己去包装和传递,主要实现newThread方法即可;
handler:无法执行任务处理策略

1、CallerRunsPolicy:如果发现线程池还在运行,就直接运行这个线程
2、DiscardOldestPolicy:在线程池的等待队列中,将头取出一个抛弃,然后将当前线程放进去。
3、DiscardPolicy:什么也不做
4、AbortPolicy:java默认,抛出一个异常:RejectedExecutionException。

从构造参数得知,

      corePoolSize,maximumPoolSize,keepAliveTime,unit这几参数是解决第1点。

      workQueue,handler,这2参数直接把设计转给程度开发者,巧妙的解决2,3点。

关于线程的销毁策略:

allowCoreThreadTimeOut   当线程<=corePoolSize 是否继续回收, 默认为false.

// 线程的核心逻辑
final void runWorker(Worker w) {

        boolean completedAbruptly = true;
        try {
            
            // getTask=null
            // 1. 当allowCoreThreadTimeOut为false,线程数大于核心线程数任务超时则返回null
            // 2. 当allowCoreThreadTimeOut为true,任务超时则返回null
            while (task != null || (task = getTask()) != null) {
                w.lock();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            // 当 getTask()=null时会执行到这条代码,表示正常结果
            // 当 beforeExecute,run, afterExecute抛出异常的时候不会执行代码
            completedAbruptly = false;
        } finally {
            // 1. 当线程异常跳出completedAbruptly=true 手工减数量
            // 2. 维护些全局的基本信息
            // 3. 通过tryTerminate,使用中Interrupt方式唤醒getTask中的阻塞(getTask已经处理了中断异常)
            // 4. tryTerminate这种传递式的唤醒中断,保存shutdown之后每个线程都能关闭。
            processWorkerExit(w, completedAbruptly);
        }
    }

 

线程池的实战

Java通过Executors提供四种线程池,分别为:

newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
corePoolSize:0
maximumPoolSize:Integer.MAX_VALUE
workQueue:SynchronousQueue(1个一直offer不进去的队列,ThreadPoolExecutor的线程数量就会一直增加)
keepAliveTime:60s

newFixedThreadPool 创建一个nThreads定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
corePoolSize:nThreads
maximumPoolSize:nThreads
workQueue:LinkedBlockingQueue(无限大)
keepAliveTime:0s

newScheduledThreadPool 创建一个nThreads定长线程池,支持定时及周期性任务执行。
corePoolSize:nThreads
maximumPoolSize:Integer.MAX_VALUE
workQueue:DelayedWorkQueue(1个时间到了就会生效任务的队列)
keepAliveTime:0s

newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
newFixedThreadPool 的nThreads=1;

 

 

一.Executor生命周期

1.线程池有运行、关闭、停止、结束四种状态,结束后就会释放所有资源

2.shutdown 与 shutdowNow 分别对应

  • 平缓关闭:已经启动的任务全部执行完毕,同时不再接受新的任务 
  • 立即关闭:取消所有正在执行和未执行的任务

3.检测线程池是否正处于关闭中,使用isShutdown()

       描述的是非RUNNING状态,也就是SHUTDOWN/STOP/TERMINATED三种状态

4.检测线程池是否已经关闭使用isTerminated()

       描述的是关闭状态,也就是TERMINATED三种状态

5.定时或者永久等待线程池关闭结束使用awaitTermination()操作

        shutdown 与 shutdowNow不是阻塞操作,只是发起关闭任务,awaitTermination则是等待到线程isTerminated()

 

二.任务生命周期

   任务定义了7种状态:   

  • NEW:新建
  • COMPLETING:完成
  • NORMAL:正常运行
  • EXCEPTIONAL:异常退出
  • CANCELLED:任务取消
  • INTERRUPTING:线程中断中
  • INTERRUPTED:线程已中断

   在这七种状态中,有四种任务终止状态:NORMAL、EXCEPTIONAL、CANCELLED、INTERRUPTED。

   以下为7种状态的关系情况

  1. NEW—>COMPLETING—>NORMAL(任务执行正常)
  2. NEW—>COMPLETING—>EXCEPTIONAL(任务执行异常)
  3. NEW—>CANCELLED(等待任务的取消)
  4. NEW—>INTERRUPTING—>INTERRUPTED(执行中任务的取消)

JAVA中,Future 表示了一个任务的生命周期。

        Future.get()

如果任务正常执行,则返回任务结果

如果任务执行异常,则会抛出执行异常

如果任务被取消,则抛出取消异常(末执行任务直接不执行,正在执行任务中断)

中断正在执行的任务

          采用interrupt()。对线程设置为中断标志。设置后不会立刻中断线程,而是待调用严格检查异常interruptException的时候就会停止,比如await,sleep等。
然而,并不是所有的阻塞都会响应中断,对于不能响应中断interruptException的阻塞。interrupt方法只能标志位可中断,但是无任何其他的作用。处理不可中断的阻塞 ,我们应该查找阻塞的原因,然后重写interrupt的方法来中断。

1.io包中的socket I/O:阻塞I/O形式为对套接字的read、write操作。虽然read,write操作不会响应中断,但是通过关闭底层的套接字,可以使得由于使用该方法而阻塞的线程抛出socketException
2.io包中的同步I/O 
3.selector的异步IO,close或wakeup的时候会使线程抛出CloseSelectorException
4.获得某个锁:如果一个线程因等待某个锁而阻塞,那么该线程是不会理会中断,唯一的办法是使用实现lock接口的reentrantlock(注释3)对象
(具体参考JAVA并发编程实战)

 Future的灵活运用 

    等待一任务超过一定时间则取消它

public  static  void timeRun(Runnable r, long timeout, TimeUnit unit){
        ExecutorService executor = Executors.newSingleThreadExecutor();

        Future task = executor.submit(r);

        try {
            task.get(timeout, unit);
        } catch (InterruptedException e) {
            //中断取消
        } catch (ExecutionException e) {
            //执行异常
        } catch (TimeoutException e) {
           //超时
        }finally {
            //如果任务超时,则直接取消,结果已没作用
            task.cancel(true);
            //true 不管任务是否执行,可以中断。false:如果任务还未执行,就不要运行。
        }
    }

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值