1、首先看下Executor获取线程池,这样方式,可以设置线程池的大小,但是了解线程池的内部原理的情况下,这样的线程池可能会引起OOM,原因在于
该线程池的等待队列最大长度默认为int的最大值,随口默写出来就是2147483647(2^31 -1,高中物理老师说过一句话,记住一些固定的数字可以预判一些问题)。线程池在提交任务时,如果线程池未达到最大线程数,则起线程执行任务,在达到最大值后,会放入等待队列,按默认的int最大值,很容易造成内存溢出。所以通常会选择自行构造线程池
ExecutorService threadPool = Executors.newFixedThreadPool(3);
2、通过自行构建线程池,指定等待队列的长度。那么问题来了,虽然用的是BlockingQueue,但是往BlockingQueue放任务时,用的是offer(),方法,而不是阻塞的方法put();这样在队列满了之后,继续往队列放任务就会抛异常,线程池提供了rejection机制去处理这种情况,应用可以自定义如何处理队列满的情况,默认是直接丢弃。对于有些业务场景,我们宁愿阻塞等待,也不要无止境的放队列,然后让他失败。这时候需要再用一点奇技淫巧,用来保证在等待队列放置一定数量后,阻塞生成任务的线程,等到线程池里已经有任务处理完了再继续放入任务。
ExecutorService threadPool = new ThreadPoolExecutor(5, 5,
0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(5*2));
3、还是使用2中自行构建的线程池,除此之外再定义一个blockQueue,最大长度为5,如下列代码。在while循环时,threadpool中的等待队列会逐渐增加最后稳定在5,并且此时与blockingQueue的长度一致,此时如果继续循环,便在blockingQueue.put(new Data())处阻塞,直到线程池中已有任务处理完。
BlockingQueue<Data> blockingQueue = new ArrayBlockingQueue<Data>(5);
@Test
public void testBlock() throws InterruptedException {
int i=0;
while(i<100){
blockingQueue.put(new Data());
// System.out.println("blockingQueue.size():"+blockingQueue.size()+" "+"threadpool:"+threadPool);
// 封装成task时并没有从blockingQueue中take,只有submit提交执行时才take,因此,等待队列中的task数目,就等于blockingQueue的长度了
threadPool.submit( ()->{return process( blockingQueue.take());});
System.out.println("blockingQueue.size():"+blockingQueue.size()+" "+"threadpool:"+threadPool);
Thread.sleep(10000L);
System.out.println("blockingQueue.size():"+blockingQueue.size()+" "+"threadpool:"+threadPool);