线程池是一种多线程处理方式,通过将任务添加到队列中,在创建线程后自动启动这些任务。线程池中的线程可以复用,可以控制最大并发数,并且方便管理,也就是说,线程池会提前创建好一些线程,当后续需要用到线程时,从线程池里拿就行,一旦任务结束,线程并不会结束,而是回归到线程池,等待下一个任务,然后继续执行。那么为什么不什么时候使用线程就什么时候创建线程呢?这样不比往线程池里面拿线程更香吗?这是错误的,使用线程池显然更香,因为使用线程池比从系统里面创建新的线程更快更高效。为什么呢?因为如果是从系统这里直接创建线程,需要调用系统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,//时间单位 BlockingQueueworkQueue,//管理任务的阻塞队列 ThreadFactory threadFactory,//ThreadFactory threadFactory是线程工厂,可以通过它来创建线程 RejectedExecutionHandler handler)//拒绝方式
首先得知道,ThreadPoolExecutor类的线程池里的线程个数并不是一成不变的,而是会根据当前任务的情况动态发生变化,但这样的变化也是有限制的,corePoolSize和maximumPoolSize就是对里面线程数目的限制,其中corePoolSize表示核心线程数,也就是最少线程数,而maximumPoolSize则表示最大线程数,任你任务再多,创建的线程数目也不能超过这个数目。当空闲的时候,又会回收掉创建的线程,但线程数目不能少于核心线程数,这样就能保证繁忙时能高效的处理问题,空闲时也不会浪费资源。long keepAliveTime和TimeUnit unit则表示允许线程空闲的时间,其中keepAlive是时间数字,如3000,而unit则是单位,如(秒,毫秒之类的),当线程空闲时间超过这个时间后,则会对该线程进行回收,避免浪费资源。BlockingQueue
1)ThreadPoolExecutor.AbortPolicy:当阻塞队列满了之后,如果继续添加,那么就会抛出异常,线程池直接不干了,不仅先前的任务不干了,新添加的任务也不干了。
2)ThreadPoolExecutor.CallerRunsPolicy:谁是添加这个任务的线程,谁去执行这个任务。
3)ThreadPoolExecutor.DiscardOldestPolicy:丢弃最早的任务,执行新的任务。
4)ThreadPoolExecutor.DiscardPolicy:丢弃新的任务。