【java并发】线程池

0、线程活跃性与安全性

活跃性:期望的多线程中的事件一定发生。活跃性问题:线程饥饿死锁。

安全性:永远不发生错误的事情。

1、线程饥饿死锁

什么是饥饿死锁?

(1)线程池大小为1,正在执行的任务向线程池提交了一个新任务,而当前的这个任务要等到新任务执行后,返回结果才会结束。这种情况会导致饥饿死锁。

(2)当线程池比较大时,线程池中正在执行的任务,都要等待线程队列中任务的执行结果。也会发生饥饿死锁。

饥饿死锁,实际上就是由于任务之间的相互依赖导致的。

2、设置线程池大小的方法

在代码中通常不会限制线程池的大小。一般可以通过配置机制设置。

原则:不宜过大,不宜过小。过大,会导致大量的线程竞争CPU,导致消耗内存,最终导致耗尽资源;过小,资源得不到充分利用,系统吞吐量较低。

设置方法:

(1)计算密集型:线程池的大小为CPU的个数加1,即:Nthread = Ncpu + 1;

(2)IO密集型:

a.基本计算方法

Nthread = Ncpu * Ucpu * (1 + W/C)

其中,

Ucpu:CPU的使用率,范围0~1

W:CPU使用过程中的等待时间

C:CPU的计算时间

b.外加其他资源的限制

如:内存、文件句柄、套接字句柄、数据库连接。

计算方法,MAX(Nthread) = 资源总大小/每个任务所需资源大小

c.受其他资源池的限制

如:数据库连接池大小的限制。数据库连接池与线程池大小,二者相互制约。

3、配置ThreadPoolExecutor

(1)各个类之间的关系

继承关系:

Executor->ExecutorService->AbstractExecutorService->ThreadPoolExecutor

(2)生成ThreadPoolExecutor

Executors框架提供了newCachedThreadPool、newfFxedThreadPool、newSingleThreadPool这些方法来生成不同内部参数的ThreadPoolExecutor。

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

newfFxedThreadPool:可以设置线程池的基本参数。

newCachedThreadPool:线程池大小无界,最大为Integer,MAX_VAULE。基本大小为1。超时时间为1min。

(3)通过任务队列加一级线程的缓冲

newfFxedThreadPool:适合使用的线程队列:有界队列。如:ArrayBlockingQueue、有界的LinkedBlockingQueue、PriorityBlockingQueue

newCachedThreadPool:适合使用无界队列:无界的LinkedBlockingQueue

(4)饱和策略

饱和策略是为有界队列设计的。

只有当有界队列被填满时,才会触发饱和策略进行任务的处理。

饱和策略有:……

调用者运行策略,既不会抛弃任务,也不会抛出异常。

将饱和后的一个任务,在调用execute的主线程执行。执行期间,不再接受新的任务进入线程池。

这种策略的过载场景会实现一种高负载情况下的平缓的性能降低。因为其过载过程是:线程池->工作队列->应用程序->TCP->客户端。

(5)ThreadPoolExecutor是通过线程工厂方法创建线程的。线程工厂可以自己定制。

猜你喜欢

转载自www.cnblogs.com/suyeSean/p/9314500.html