上面是线程的执行周期
这个线程的生命周期,可以看到时间都浪费在了创建和销毁的这里了.
实际上执行业务的时间只有1s
在高并发的时候,如果持续的去,创建,销毁,那么是很浪费时间的
这个时候就需要用线程池
用了线程池以后,线程的创建用的3秒,销毁用的2秒就没有了,
只剩运行时间用的1s了.
比如有100个任务要执行,那么我们的线程池是10个线程,
那么也就是说,线程池已经准备好10个线程去用了,
这10个线程同时去执行10个任务,那么剩余的90个任务,因为咱们用的是阻塞队列
所以剩余的90个任务就会阻塞等待执行.
可以看一下这个线程队列的工作过程,
首先,这里
ThreadPoolExecutor t=new ThreadPoolExecutor(1,3,0,TimeUnit.MILISECONDS,new LinkedBlockingDeque<Runnable>(3));
这里1,是核心线程数,就是在线程池中活动的线程数,然后3是最大线程数,也就是可以开启的最大线程数,
然后0是超时时间,后面是这个超时时间的单位,这个超时时间的意思是,除了核心线程,我另外创建的线程,如果没有任务添加进来的话,那么线程需要等多久
被销毁掉,然后后面的new LinkedBlockingDeque<Runnable>(3)是一个阻塞队列.
3是队列的大小,这里如果队列的大小,不填写的话,那么,默认就是Integer.MAX_VALUE这个数
2147483647 21亿多
然后再看一下这个线程池队列的工作过程:
比如一个任务来了,因为一个任务就等于一个线程,那么就要先判断,是否现在正在工作的线程个数,大于核心线程数(已经创建好的可以用的线程数)了,
如果没有大于核心线程数,那么就从就绪的线程中拿一个,去执行任务就可以了,如果大于了核心线程数,那么,再去判断,
任务放在队列中,这个队列的大小满了没,比如说3个,队列中的任务大于3个了嘛,没有大于3个,就把这个任务继续放到这个
队列中去,然后等待,正在执行的线程,用完了以后空闲下来,再去执行队列里面的任务,如果
任务往队列中放的时候,发现队列已经满了,那么这个时候,就可以创建新线程来执行任务,这个时候再做个判断,
如果创建的新线程数量+核心线程数量<=最大线程数的时候,那么就利用新创建的线程和核心线程来分摊任务,
如果新线程数量+核心线程数量>最大线程数量的话,那么,这个时候程序就会报错,这个在实际应用中我们发生过,
就会拒绝这个任务的加入.
这个超时时间的意思是,当队列中的任务已经满了,并且核心线程也都在用着,那么我去创建新线程,
那么,我创建的这个新的线程,执行完这个队列中溢出的任务以后,这个线程不会立刻销毁,我们这个线程
需要等等看,还有没有新线程,如果有的话我就继续执行去,如果没有我就等等,等多久销毁,这里是指定的这个时间.
接下来用代码,弄一下上面的过程:
这里创建一个线程池,指定,核心线程数1,最大线程数2,新创建线程超时时间3,秒,然后用阻塞队列,队列大小是3
这里我执行一个任务,注意用完线程池,要关闭
这个时候核心线程,在执行这个任务,然后
这个时候队列中是空的,因为一个线程已经从队列中取出,正在执行
那么我又放了3个任务,这个时候,队列正好是满的了
可以看到这里,打印出了4个,都是用第一个线程执行的.
因为第一个线程正在执行第一个任务,然后第2,3,4个任务正好在队列中,
队列还能容得下,所以也没有创建新线程.
然后如果我再添加一个任务,那么就是,
第一个核心线程正在执行第一个任务,然后第2,3,4个任务在队列中,然后如果这个时候,再来一个任务
第5个任务,这个时候队列中也不能放了,这个时候,就会
再创建一个新线程来执行,咱们看看
可以看到第5个任务是用thread2来执行的.
然后再执行可以看到,有几个任务是用thread2来执行的,这个就是说,咱们上面说的
核心线程会和新创建的线程分摊这任务,所以只要是谁有时间谁就去执行这个任务.
那么如果,咱们再去添加任务呢?
可以看到,第一个核心线程去执行第一个任务
然后,第2,3,4个任务放在容量为3个阻塞队列里,然后
第5个任务来的时候,阻塞队列容量不够了会新创建一个线程去执行,
然后第6个任务来的时候,因为咱们最大线程数设置的是2,现在已经是2个线程了,所以这个时候
就会拒绝服务,报错了.
这个就是线程池的应用.