总结一下最近工作中遇到的线程池相关问题

总结一下最近工作中遇到的线程池相关问题

背景:

在业务代码中,新建线程池的代码如下:

ThreadPoolExecutor myThreadPool = new ThreadPoolExecutor(0, 100, 0L, 
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());

我们的配置是
corePoolSize=0;
maximumPoolSize=100;
keepAliveTime,=0L;
使用LinkedBlockingQueue作为线程池的阻塞队

在运行后发现,代码中一共提交了5个task,只有第一个submit的task被成功执行了,其他的都没有正常运行。
通过调用getActiveCount(),getTaskCount();方法。获取到的结果同观察到的一样,active的task数量为1,task的数量总数为5。
接下来,我们调用getQueue()方法,发现queue的size为4,这与我们的总数-被执行线程数刚好契合。遍历queue中的task后,发现其他的task果然都在阻塞队列中被阻塞住了。

问题分析

通过网上查资料和阅读源码,了解到如下信息。
线程池在接收到新的task后,处理逻辑如下:

  1. 首先判断当前的线程数量是否小于corePoolSize,如果是,线程池会创建新的核心线程(coreThread)执行task;
  2. 如果线程池中的线程数量大于corePoolSize,会将该task放入我们设置的阻塞队列中。
  3. 如果阻塞队列也满了,且线程中线程数量小于maximumPoolSize;线程池创建非核心线程执行task(新创建的非核心线程会直接执行当前被submit的task,而不是先从阻塞队列中尝试获取);
  4. 当非核心线程运行结束,会从阻塞队列中尝试获取task,如果获取不到,经过我们设置的keepAliveTime后,非核心线程会被回收。
    在这里插入图片描述

由于我们将corePoolSize设为了0,线程池会首先执行addWorker方法,创建新的临时线程来执行task;由于我的每个task里面的业务逻辑都包含一个while循环(需要不断从数据库中抓取数据)。因此,第一个task永远不会结束,当后面的四个task被提交到线程池后,由于当前线程数已经大于corePoolSize,新的任务将会被放入阻塞队列中,永远不会被执行到。

 if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }

解决办法:

开始的时候,想的是既然阻塞队列满了之后,新提交的task才会创建非核心线程去执行,,那我不如将阻塞队列置为空.但设置后,代码却报错了,查看源码后发现线程池在这里是有限制的。

If (workQueue == null || threadFactory == null
		 || handler == null)
            throw new NullPointerException();

再继续查阅相关资料,发现对于我的这种情况,更适合使用SynchronousQueue作为阻塞队列。

SynchronousQueue
也是一个队列来的,但它的特别之处在于它内部没有容器,一个生产线程,当它生产产品(即put的时候),如果当前没有人想要消费产品(即当前没有线程执行take),此生产线程必须阻塞,等待一个消费线程调用take操作,take操作将会唤醒该生产线程,同时消费线程会获取生产线程的产品(即数据传递)

简单来讲就是,SynchronousQueue其实是一个虚假的“队列”,它没有容量,也不会存储元素每一个put操作必须要等待一个take操作,否则不能继续添加元素,反之亦然。
当我们使用SynchronousQueue作为阻塞队列后,线程池的执行逻辑则变为了,只要当前线程数小于maximumpoolsize,线程池都会创建新的线程去执行新提交的task(小于corepoolsize的创建核心线程,大于的创建非核心线程)。当线程数大于最大线程数后,线程池会执行相应的饱和策略。

后记:

后面还遇到了执行完的线程没有及时被回收的问题,等过两天总结下继续发出来。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PythonThreadPoolExecutor是线程池的一种实现方式,它提供了方便的接口来进行并发编程。在使用ThreadPoolExecutor时,通常会遇到异常捕获的问题。 当线程池的线程执行任务时,如果任务发生异常,异常会被捕获,并通过Future对象返回给调用者。我们可以通过检查Future对象的状态来获取异常信息。Future对象是一个表示异步计算结果的对象,它可以用来检查任务是否完成、取消任务、获取任务的结果等。 在ThreadPoolExecutor,可以通过submit方法来提交任务。这个方法返回一个Future对象,我们可以通过调用Future对象的result方法来等待任务完成并获取任务的结果。如果任务发生异常,result方法将会抛出异常,并将异常的类型和信息传递给调用者。 另外,我们还可以通过调用ThreadPoolExecutor的shutdown方法来关闭线程池。关闭线程池后,任何待处理的任务将会被取消,并且已提交但还未开始执行的任务将会被清除。我们可以通过调用Future对象的cancel方法来取消任务。 在代码,我们可以使用try-except语句块来捕获线程任务的异常。可以使用ThreadPoolExecutor的submit方法来提交任务,并通过返回的Future对象来获取任务的结果。在调用Future对象的result方法时,如果发生了异常,可以使用try-except语句块来捕获异常并处理异常。另外,在使用完线程池后,我们应该调用shutdown方法来关闭线程池,以释放资源。 总结起来,Python的ThreadPoolExecutor提供了异常捕获机制,我们可以通过检查返回的Future对象来获取任务执行过程的异常信息。在使用完线程池后,我们应该及时关闭线程池,以释放资源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值