java多线程:线程池原理、阻塞队列 (3)

实现非常简单,那就是如果说 e 这个线程池已经 shutdown 了,那么就什么也不干,也就是这个任务直接丢了;否则,r.run() ,相当于调用这个方法的线程里直接执行了这个 Runnable 任务。

此时我们可以把 1.3 里的代码修改一下,只修改策略为
CallerRunsPolicy:

java多线程:线程池原理、阻塞队列

可以看到,有些任务会在 main 线程里处理。

2.2.2 AbortPolicy:终止策略。

抛异常。前面已经试过了,这个是默认的拒绝策略。

java多线程:线程池原理、阻塞队列

2.2.3 DiscardPolicy:丢弃任务。

可以看到,源码里就是是什么也不做。如果场景中允许任务丢失,这个是最好的策略。

java多线程:线程池原理、阻塞队列

2.2.4 DiscardOldestPolicy:抛弃队列中等待最久的任务。

抛弃队列中等待最久的任务,然后把当前的任务加入队列中,尝试再次提交当前任务。

源码里也就是利用队列操作,进行一次出队操作,然后重新调用 execute 方法。

java多线程:线程池原理、阻塞队列

2.3 线程池的五种状态

一个正常的线程的生命周期区别开,这个是线程池里线程的状态。

Running,能接受新任务以及处理已添加的任务;

Shutdown,不接受新任务,可以处理已经添加的任务,也就是不能再调用execute或者submit了;

Stop,不接受新任务,不处理已经添加的任务,并且中断正在处理的任务;

Tidying,所有的任务已经终止,CTL记录的任务数量为0,CTL负责记录线程池的运行状态与活动线程数量;

Terminated,线程池彻底终止,则线程池转变为terminated的状态。

java多线程:线程池原理、阻塞队列

如图所示,从running状态转换为 shutdown,调用 shutdown()方法;如果调用shutdownNow()方法,就直接会变成stop。

terminated()是钩子函数,默认是什么也不做的,我们可以重写,然后决定结束之前要做一些别的处理逻辑。这个钩子函数,就是模板模式的方法。

三、阻塞队列

线程池里的 BlockingQueue,阻塞队列,事实上在消费者生产者问题里的管程法实现,我们的策略也是类似阻塞队列的,用它来做一个缓存池的作用。

阻塞队列:任意时刻,不管并发有多高,永远保证只有一个线程能够进行队列的入队或出队操作。也就意味着他是能够保证线程安全的。

另外,阻塞队列分为有界和无界队列,理论上来说一个是队列的size有固定,另一个是无界的。对于有界队列来说,如果队列存满,只能出队了,入队操作就只能阻塞。

在 juc 包里,阻塞队列的实现有很多:

ArrayBlockingQueue:有界阻塞队列;

LinkedBlockingQueue:链表结构(大小默认值为Integer.MAX_VALUE)的阻塞队列;

PriorityBlockingQueue:支持优先级排序的无界阻塞队列;

DelayQueue:使用优先级队列实现的延迟无界阻塞队列;

SynchronousQueue:不存储元素的阻塞队列,相当于只有一个元素;

LinkedTransferQueue:链表组成的无界阻塞队列;

LinkedBlockingDeque:链表组成的双向阻塞队列。

对于 BlockingQueue 来说,核心操作主要有几类:插入、删除、查找。

java多线程:线程池原理、阻塞队列

其中的四种异常策略:

抛异常:如果阻塞队列满,再往队列里 add 插入元素会抛 IllegalStateException:Queue full,如果阻塞队列空,再 remove 就会抛 NoSuchElementException。

特殊值:offer 方法:成功 true,失败 false,poll 方法,成功就返回元素,没有就返回 null。

阻塞:阻塞队列满的时候,生产者线程继续 put 元素,队列就会阻塞直到可以 put 数据或者响应中断然后退出,阻塞队列空的时候,消费者线程继续 take 元素,队列就会一直阻塞直到有元素可以 take。

超时退出:阻塞队列满的时候,会阻塞生产者线程且超时退出,空的时候会阻塞消费者线程且超时退出。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzjwdd.html