关于线程池

        线程池是一种多线程处理方式,通过将任务添加到队列中,在创建线程后自动启动这些任务。线程池中的线程可以复用,可以控制最大并发数,并且方便管理,也就是说,线程池会提前创建好一些线程,当后续需要用到线程时,从线程池里拿就行,一旦任务结束,线程并不会结束,而是回归到线程池,等待下一个任务,然后继续执行。那么为什么不什么时候使用线程就什么时候创建线程呢?这样不比往线程池里面拿线程更香吗?这是错误的,使用线程池显然更香,因为使用线程池比从系统里面创建新的线程更快更高效。为什么呢?因为如果是从系统这里直接创建线程,需要调用系统api,进一步的由操作系统内核,完成线程的创建过程,但是,由于内核是为所有的线程服务的,什么时候创建线程由系统内核决定,如果在系统内核繁忙时创建,则需要等待一定的时间使得内核有空闲时在创建,因此它是不可控的,而如果是从线程池这里获取线程,上述的内核中进行的操作,都是提前做好的,就算系统繁忙,也可以直接获取线程,不需要等待,因为它是直接创建好的,现在取线程的过程,纯粹的用户代码完成(纯用户态),它则是可控的。线程池有三种创建方式:单一线程池、固定大小线程池和可伸缩线程池。单一线程池只有一个线程,固定大小线程池指定了线程池的大小,而可伸缩线程池可以根据需要动态改变线程池的大小。线程池的核心组成是线程和任务,线程是前面学过的线程,任务是实现了Runnable或Callable接口的实例对象。线程池的代码实现可以使用Executor框架中的Executors类来创建,也可以使用ThreadPoolExecutor类来自定义线程池。

ExecutorService service = Executors.newFixedThreadPool(4);//ExecutorService service是线程池的对象,通过Executors里的方法来创建,创建了有4个线程的线程池。
ExecutorService service1 = Executors.newCachedThreadPool();//创建线程动态变化的线程池,已经创建了的线程不会回收。
ExecutorService service2 = Executors.newSingleThreadExecutor();//创建只有单个线程的线程池,与直接创建线程相比,更为方便。

线程池通过submit来添加任务,参数为Runnable对象,

service.submit(new Runnable() {
    @Override
    public void run() {
        System.out.println("haha");
    }
});

除此之外,标准库还提供了一个接口更为丰富的线程池类:ThreadPoolExecutor,上述的线程池创建方法,其实是对ThreadPoolExecutor的各个不同的创建方法而进行的封装。

        ThreadPoolExecutor有多个构造方法,但只需要掌握这一个,就可以掌握所有的构造方法,这一个构造方法就是

public ThreadPoolExecutor(int corePoolSize,//核心线程数
                          int maximumPoolSize,//最大线程数
                          long keepAliveTime,//线程最大空闲时间
                          TimeUnit unit,//时间单位
                          BlockingQueue workQueue,//管理任务的阻塞队列
                          ThreadFactory threadFactory,//ThreadFactory threadFactory是线程工厂,可以通过它来创建线程
                          RejectedExecutionHandler handler)//拒绝方式

首先得知道,ThreadPoolExecutor类的线程池里的线程个数并不是一成不变的,而是会根据当前任务的情况动态发生变化,但这样的变化也是有限制的,corePoolSize和maximumPoolSize就是对里面线程数目的限制,其中corePoolSize表示核心线程数,也就是最少线程数,而maximumPoolSize则表示最大线程数,任你任务再多,创建的线程数目也不能超过这个数目。当空闲的时候,又会回收掉创建的线程,但线程数目不能少于核心线程数,这样就能保证繁忙时能高效的处理问题,空闲时也不会浪费资源。long keepAliveTime和TimeUnit unit则表示允许线程空闲的时间,其中keepAlive是时间数字,如3000,而unit则是单位,如(秒,毫秒之类的),当线程空闲时间超过这个时间后,则会对该线程进行回收,避免浪费资源。BlockingQueue workQueue则是要执行的任务,线程池中有很多任务,它们可以使用阻塞队列来进行管理,这个阻塞队列既可以是线程池内置的,也可以是手动指定的,如根据优先级来指定一个优先级队列。ThreadFactory threadFactory是线程工厂,可以通过它来创建线程,可以通过不同ThreadFactory来来创建不同的线程。RejectedExecutionHandler handler是这里的重点,它的含义是拒绝方式或者拒绝策略。当阻塞队列满了后,继续添加任务,该如何应对,你或许会问:让它在这里等待不就好了吗?这样的方法可行,但并不适合所有情况,因此,得需要一个确切的方式,以下就是拒绝方式。

1)ThreadPoolExecutor.AbortPolicy:当阻塞队列满了之后,如果继续添加,那么就会抛出异常,线程池直接不干了,不仅先前的任务不干了,新添加的任务也不干了。

2)ThreadPoolExecutor.CallerRunsPolicy:谁是添加这个任务的线程,谁去执行这个任务。

3)ThreadPoolExecutor.DiscardOldestPolicy:丢弃最早的任务,执行新的任务。

4)ThreadPoolExecutor.DiscardPolicy:丢弃新的任务。

你可能感兴趣的:(java,开发语言)