1.为什么要使用线程池?
在多线程的时候,如果不使用线程池而直接new Thread 或者实例化RunAble接口.在一些重复的请求时候,线程经常创建/结束.这样十分消耗资源,对速度也有影响.
使用线程池,相当于让线程池来管理线程的生命周期.也就是说,线程启动执行完之后,不再消亡二十处于空闲状态.再次有请求的时候,再启动该线程.并改变该线程的状态.这样,减少线程的反复创建.
2.线程池的使用
1. JDK中可以查到这些,而对于每个接口的说明,以及使用都有相应介绍.目前使用的最多的是ExecutorService与Executor接口.Executor与ExecutorService最大的区别在于后者可以启动有返回值的线程.更加丰富些.而ScheduledExecutorService是带延迟启动的线程池,一般用于定时任务.
2. Executor与ExecutorService,推荐使用后者.因为ExecutorService是对Executor的继承.所以对于没有返回值的线程,后者使用execute方法启动线程.与Executor的execute一样.
3. 线程池的新建方式常用的有ThreadPoolExecutor与Executors两个类.
1.Executors类创建线程池
1. newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
2. newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。 3. newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。 4. newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。代码:
public class ThreadPool { //多线程共用变量static int time = 0 ; public static void main(String[] args) { //创建线程池 ExecutorService executor = Executors.newFixedThreadPool(10) ; //创建线程 Runnable runnable = new Runnable() { @Override public void run() { try { Thread.sleep(100); time++ ; System.out.println("Start Thread:\t"+time); } catch (InterruptedException e) { } } }; //执行超过给定线程池的线程数目 for (int i = 0; i < 20; i++) { executor.execute(runnable); } //主线程等待线程结束 while (((ThreadPoolExecutor) executor).getActiveCount() != 0) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("执行次数:\t" + time); executor.shutdown(); } }
输出结果:
Start Thread: 4Start Thread: 9Start Thread: 8Start Thread: 6Start Thread: 7Start Thread: 7Start Thread: 4Start Thread: 4Start Thread: 4Start Thread: 10Start Thread: 11Start Thread: 15Start Thread: 14Start Thread: 14Start Thread: 14Start Thread: 17Start Thread: 18Start Thread: 17Start Thread: 19Start Thread: 20执行次数: 20
这里没有1,是因为异步多线程的原因.
2.ThreadPoolExecutor类创建线程池.
public class ThreadPool { //多线程共用变量static int time = 1 ; public static void main(String[] args) { //创建有边界的数组队列(FIFO) BlockingQueue< Runnable> queue = new ArrayBlockingQueue(10, true) ; //创建线程池,核心线程池大小为5,最大线程池为8.传入执行队列大小为10 ExecutorService executor = new ThreadPoolExecutor(5, 8, 10000, TimeUnit.HOURS, queue) ; //创建线程 Runnable runnable = new Runnable() { @Override public void run() { time ++ ; System.out.println("Thread Start:\t"+time); } }; //加入队列 for (int i = 0; i < 10; i++) { queue.add(runnable) ; } //执行队列里的线程 for (int i = 0; i < 20; i++) { if(queue != null && queue.peek() != null){ executor.submit(queue.poll()) ; } } //主线程等待线程结束 while (((ThreadPoolExecutor) executor).getActiveCount() != 0) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("执行次数:\t" + time); executor.shutdown(); } }
输出结果:
Thread Start: 3Thread Start: 5Thread Start: 6Thread Start: 3Thread Start: 4Thread Start: 7Thread Start: 8Thread Start: 11Thread Start: 10Thread Start: 9执行次数: 11
这里执行次数为11,是因为队列只有10个.
ps:这里可能会混淆,有一个core数量,max数量,队列数量.core与max是多线程里面的.队列的是队列里面的..感觉废话,不过不理解的话重复看一下.线程运行与队列无关,core数量是线程池最小数量,如果少于这个数量,线程池会自动创建新的线程加入线程池.如果线程池大于最大数量,则等待线程空闲.等待参数TimeUnit枚举类型.这里数量不要混淆了.
使用上来说,Executors获取线程池比较简单快速,但是性能上肯定不如new ThreadPollExecutor()处理线程.而且后者支持队列.关于队列,我后面文章有讲..