JAVA - 并发 - 线程池
线程池
自定义线程池
1.降低资源消耗:通过复用已创建的线程,避免频繁地创建和销毁线程所带来的开销。线程的创建与销毁涉及操作系统层面的操作,如分配栈空间、初始化上下文等,这些操作在高并发场景下会显著消耗系统资源。
2.提高响应速度:当任务到达时,可以直接从线程池中获取一个空闲线程来执行任务,而无需等待新线程的创建过程,从而加快了任务的响应速度。
实现
Thread pool是线程池,Blocking Queue是任务阻塞队列,主线程、t1、t2生产任务,线程池消费任务

示例代码:之前的疑问,ThreadPool类的execute方法,多线程同时执行了该方法,方法里有synchronized保护,没有线程安全的问题,
解释下面的代码的运行,2个执行任务的线程,5个任务,程序启动,2个任务同时被任务的线程执行,3个进入队列,等待执行。主线程在产生任务,那2个执行任务的线程在消费执行。
@Slf4j(topic = "c.TestPool")
public class TestPool {
public static void main(String[] args) {
ThreadPool threadPool=new ThreadPool(2,1000, TimeUnit.MILLISECONDS,10);
for(int i=0;i<5;i++){
int j=i;
threadPool.execute(()->{
log.debug("{}",j);
});
}
}
}
@Slf4j(topic = "c.ThreadPool")
class ThreadPool{
//任务类
private BlockQueue<Runnable> taskQueue;
//线程集合
private HashSet<Worker> workers=new HashSet();
//核心线程数
private int coreSize;
//获取任务的超时时间
private long timeout;
private TimeUnit timeUnit;
public ThreadPool(int coreSize, long timeout, TimeUnit timeUnit,int queueCapacity) {
this.coreSize = coreSize;
this.timeout = timeout;
this.timeUnit = timeUnit;
taskQueue=new BlockQueue<>(queueCapacity);
}
public void execute(Runnable task){
synchronized (workers){
if (workers.size()<coreSize){
Worker worker=new Worker(task);
workers.add(worker);
log.debug("新增 worker{},{}",worker,task);
worker.start();
}else {
log.debug("加入任务队列 {}",task);
taskQueue.put(task);
}
}
}
class Worker extends Thread{
private Runnable task;
public Worker(Runnable task){
this.task = task;
}
@Override
public void run() {
while (task != null || (task=taskQueue.take())!=null){
try {
log.debug("正在执行.....{}",task);
task.run();
}catch (Exception e){
e.printStackTrace();
}finally {
task=null;
}
}
synchronized (workers){
log.debug("worker 被移除 {}",this);
workers.remove(this);
}
}
}
}
class BlockQueue<T>{
//任务队列,一个一个的任务
private Deque<T> queue=new ArrayDeque<>();
//锁,多线程消费任务,需要加锁,生产者添加任务时也需要加锁,比如,容量5,任务队列有4个,两个线程同时往队列加,就爆容量了
private ReentrantLock lock=new ReentrantLock();
//生产者条件变量,队列满时生产者需要进入等待
private Condition fullWaitSet = lock.newCondition();
//消费者条件变量,队列没有任务时,消费者需要进入等待
private Condition emptyWaitSet = lock.newCondition();
//队列的容量上限
private int capacity;
public BlockQueue(int capacity) {
this.capacity = capacity;
}
//阻塞获取,设置超时时间
public T poll(long timeout, TimeUnit unit){
//如果没有锁,queue.removeFirst(),有可能获取到同一个任务,或者是这种情况,队列有1个任务,线程1获取到任务,线程2获取到空的
lock.lock();
try {
long nanos = unit.toNanos(timeout);
//队列为空,进入休息,等待唤醒
while (queue.isEmpty()){
try {
//睡眠超时后结束等待
if (nanos<=0){
return null;
}
//设置等了多少时间,返回的是虚假唤醒时的剩余时间,比如设为5s,睡眠了4s时刻,被虚假唤醒了,剩余1s
nanos = emptyWaitSet.awaitNanos(nanos);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//消费任务
T t = queue.removeFirst();
//唤醒生产者
fullWaitSet.signal();
return t;
}finally {
lock.unlock();
}
}
//阻塞获取
public T take(){
//如果没有锁,queue.removeFirst(),有可能获取到同一个任务,或者是这种情况,队列有1个任务,线程1获取到任务,线程2获取到空的
lock.lock();
try {
//队列为空,进入休息,等待唤醒
while (queue.isEmpty()){
try {
emptyWaitSet.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//消费任务
T t = queue.removeFirst();
//唤醒生产者
fullWaitSet.signal();
return t;
}finally {
lock.unlock();
}
}
//阻塞添加,为什么要while,自己体会
public void put(T element){
try {
lock.lock();
while (queue.size() == capacity){
try {
fullWaitSet.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//添加任务
queue.addLast(element);
//唤醒消费者
emptyWaitSet.signal();
}finally {
lock.unlock();
}
}
//获取队列大小
public int size(){
try {
lock.lock();
return queue.size();
}finally {
lock.unlock();
}
}
}
避免一直等待
public static void main(String[] args) {
//设置超时时间,避免一直等待
ThreadPool threadPool=new ThreadPool(2,2000, TimeUnit.MILLISECONDS,10);
for(int i=0;i<5;i++){
int j=i;
threadPool.execute(()->{
log.debug("{}",j);
});
}
}

输出如下:
15:34:54.651 [main] c.ThreadPool - 新增 workerThread[Thread-0,5,main],org.example.xiancheng.study13_xian_cheng_chi.TestPool$$Lambda$1/0x00000007c00f6828@7fad8c79
15:34:54.653 [main] c.ThreadPool - 新增 workerThread[Thread-1,5,main],org.example.xiancheng.study13_xian_cheng_chi.TestPool$$Lambda$1/0x00000007c00f6828@5606c0b
15:34:54.653 [main] c.ThreadPool - 加入任务队列 org.example.xiancheng.study13_xian_cheng_chi.TestPool$$Lambda$1/0x00000007c00f6828@80ec1f8
15:34:54.653 [main] c.ThreadPool - 加入任务队列 org.example.xiancheng.study13_xian_cheng_chi.TestPool$$Lambda$1/0x00000007c00f6828@1445d7f
15:34:54.653 [Thread-0] c.ThreadPool - 正在执行.....org.example.xiancheng.study13_xian_cheng_chi.TestPool$$Lambda$1/0x00000007c00f6828@7fad8c79
15:34:54.653 [main] c.ThreadPool - 加入任务队列 org.example.xiancheng.study13_xian_cheng_chi.TestPool$$Lambda$1/0x00000007c00f6828@6a396c1e
15:34:54.653 [Thread-1] c.ThreadPool - 正在执行.....org.example.xiancheng.study13_xian_cheng_chi.TestPool$$Lambda$1/0x00000007c00f6828@5606c0b
15:34:54.653 [Thread-1] c.TestPool - 1
15:34:54.653 [Thread-0] c.TestPool - 0
15:34:54.653 [Thread-1] c.ThreadPool - 正在执行.....org.example.xiancheng.study13_xian_cheng_chi.TestPool$$Lambda$1/0x00000007c00f6828@80ec1f8
15:34:54.653 [Thread-1] c.TestPool - 2
15:34:54.653 [Thread-0] c.ThreadPool - 正在执行.....org.example.xiancheng.study13_xian_cheng_chi.TestPool$$Lambda$1/0x00000007c00f6828@1445d7f
15:34:54.653 [Thread-1] c.ThreadPool - 正在执行.....org.example.xiancheng.study13_xian_cheng_chi.TestPool$$Lambda$1/0x00000007c00f6828@6a396c1e
15:34:54.653 [Thread-0] c.TestPool - 3
15:34:54.653 [Thread-1] c.TestPool - 4
15:34:56.663 [Thread-1] c.ThreadPool - worker 被移除 Thread[Thread-1,5,main]
15:34:56.663 [Thread-0] c.ThreadPool - worker 被移除 Thread[Thread-0,5,main]
当队列满时,避免一直在等待添加任务
示例,模拟让任务执行很久(100000毫秒),队列容量10,执行15个任务,程序启动,第13个任务等待加入任务队列


输出如下,第13个任务一直在等着进入任务队列

解决方法,新增带超时的队列添加的方法
//带超时的阻塞添加
public boolean offer(T element,long timeout,TimeUnit timeUnit){
lock.lock();
try {
long nanos = timeUnit.toNanos(timeout);
while (queue.size() == capacity){
try {
if (nanos<=0){
//等待超时返回false
return false;
}
log.debug("等待加入任务队列 {}",element);
nanos = fullWaitSet.awaitNanos(nanos);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//添加任务
queue.addLast(element);
log.debug("加入任务队列 {}",element);
//唤醒消费者
emptyWaitSet.signal();
//没有超时等待,添加任务返回true
return true;
}finally {
lock.unlock();
}
}
拒绝策略
在idea上慢慢看
点击查看代码
@Slf4j(topic = "c.TestPool")
public class TestPool {
public static void main(String[] args) {
//设置超时时间,避免一直等待
ThreadPool threadPool=new ThreadPool(1,1000, TimeUnit.MILLISECONDS,1,(queue, task) -> {
//queue.put(task);
boolean result = queue.offer(task, 500, TimeUnit.MILLISECONDS);
log.debug("result:{}",result);
});
for(int i=0;i<3;i++){
int j=i;
threadPool.execute(()->{
log.debug("{}",j);
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
}
}
}
@FunctionalInterface
interface RejectPolicy<T>{
void reject(BlockQueue<T> queue,T task);
}
@Slf4j(topic = "c.ThreadPool")
class ThreadPool{
//任务类
private BlockQueue<Runnable> taskQueue;
//线程集合
private HashSet<Worker> workers=new HashSet();
//核心线程数
private int coreSize;
//获取任务的超时时间
private long timeout;
private TimeUnit timeUnit;
private RejectPolicy<Runnable> rejectPolicy;
public ThreadPool(int coreSize, long timeout, TimeUnit timeUnit,int queueCapacity,RejectPolicy<Runnable> rejectPolicy) {
this.coreSize = coreSize;
this.timeout = timeout;
this.timeUnit = timeUnit;
this.taskQueue=new BlockQueue<>(queueCapacity);
this.rejectPolicy=rejectPolicy;
}
public void execute(Runnable task){
synchronized (workers){
if (workers.size()<coreSize){
Worker worker=new Worker(task);
workers.add(worker);
log.debug("新增 worker{},{}",worker,task);
worker.start();
}else {
//taskQueue.put(task);
taskQueue.tryPut(rejectPolicy,task);
}
}
}
class Worker extends Thread{
private Runnable task;
public Worker(Runnable task){
this.task = task;
}
@Override
public void run() {
while (task != null || (task=taskQueue.poll(timeout,timeUnit))!=null){
try {
log.debug("正在执行.....{}",task);
task.run();
}catch (Exception e){
e.printStackTrace();
}finally {
task=null;
}
}
synchronized (workers){
log.debug("worker 被移除 {}",this);
workers.remove(this);
}
}
}
}
@Slf4j(topic = "c.BlockQueue")
class BlockQueue<T>{
//任务队列,一个一个的任务
private Deque<T> queue=new ArrayDeque<>();
//锁,多线程消费任务,需要加锁,生产者添加任务时也需要加锁,比如,容量5,任务队列有4个,两个线程同时往队列加,就爆容量了
private ReentrantLock lock=new ReentrantLock();
//生产者条件变量,队列满时生产者需要进入等待
private Condition fullWaitSet = lock.newCondition();
//消费者条件变量,队列没有任务时,消费者需要进入等待
private Condition emptyWaitSet = lock.newCondition();
//队列的容量上限
private int capacity;
public BlockQueue(int capacity) {
this.capacity = capacity;
}
//阻塞获取,设置超时时间
public T poll(long timeout, TimeUnit unit){
//如果没有锁,queue.removeFirst(),有可能获取到同一个任务,或者是这种情况,队列有1个任务,线程1获取到任务,线程2获取到空的
lock.lock();
try {
long nanos = unit.toNanos(timeout);
//队列为空,进入休息,等待唤醒
while (queue.isEmpty()){
try {
//睡眠超时后结束等待
if (nanos<=0){
return null;
}
//设置等了多少时间,返回的是虚假唤醒时的剩余时间,比如设为5s,睡眠了4s时刻,被虚假唤醒了,剩余1s
nanos = emptyWaitSet.awaitNanos(nanos);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//消费任务
T t = queue.removeFirst();
//唤醒生产者
fullWaitSet.signal();
return t;
}finally {
lock.unlock();
}
}
//阻塞获取
public T poll(){
//如果没有锁,queue.removeFirst(),有可能获取到同一个任务,或者是这种情况,队列有1个任务,线程1获取到任务,线程2获取到空的
lock.lock();
try {
//队列为空,进入休息,等待唤醒
while (queue.isEmpty()){
try {
emptyWaitSet.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//消费任务
T t = queue.removeFirst();
//唤醒生产者
fullWaitSet.signal();
return t;
}finally {
lock.unlock();
}
}
//阻塞添加,为什么要while,自己体会
public void put(T element){
try {
lock.lock();
while (queue.size() == capacity){
try {
log.debug("等待加入任务队列 {}",element);
fullWaitSet.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//添加任务
queue.addLast(element);
log.debug("加入任务队列 {}",element);
//唤醒消费者
emptyWaitSet.signal();
}finally {
lock.unlock();
}
}
//带超时的阻塞添加
public boolean offer(T element,long timeout,TimeUnit timeUnit){
lock.lock();
try {
long nanos = timeUnit.toNanos(timeout);
while (queue.size() == capacity){
try {
log.debug("等待加入任务队列 {}",element);
if (nanos<=0){
//等待超时返回false
return false;
}
nanos = fullWaitSet.awaitNanos(nanos);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//添加任务
queue.addLast(element);
log.debug("加入任务队列 {}",element);
//唤醒消费者
emptyWaitSet.signal();
//没有超时等待,添加任务返回true
return true;
}finally {
lock.unlock();
}
}
//获取队列大小
public int size(){
try {
lock.lock();
return queue.size();
}finally {
lock.unlock();
}
}
public void tryPut(RejectPolicy<T> rejectPolicy, T task) {
lock.lock();
try {
if (queue.size() == capacity){
rejectPolicy.reject(this,task);
}else {
//添加任务
queue.addLast(task);
log.debug("加入任务队列 {}",task);
//唤醒消费者
emptyWaitSet.signal();
}
}finally {
lock.unlock();
}
}
}
ThreadPoolExecutor

线程池状态
ThreadPoolExecutor 使用 int 的高 3 位来表示线程池状态,低 29 位表示线程数量。
RUNNING,线程池创建时的状态。
SHUTDOWN,调用线程池的shutdown方法后的状态,线程池会继续运行正在运行的任务,以及剩余的任务
STOP,shutdownNow方法,跟上面不同,会中断。
| 状态 | 高3位 | 接收新任务 | 处理阻塞任务队列 | 说明 |
|---|---|---|---|---|
| RUNNING | 111 | Y | Y | |
| SHUTDOWN | 000 | N | Y | 不接收新任务,但处理阻塞队列剩余任务 |
| STOP | 001 | N | N | 中断正在执行的任务,并抛弃阻塞队列任务 |
| TIDYING | 010 | - | - | 任务全执行完毕,活动线程为 0 即将进入终结 |
| TERMINATED | 011 | - | - | 终止状态 |

ThreadPoolExecutor的相关参数说明
核心线程数,线程池创建时设置的核心线程数目。
最大线程,救急线程数 + 核心线程

如下图所示,虚线表示线程还未创建,2个核心,1个救急,最大线程数目3。

现在有任务1要执行,创建了一个核心线程(实线表示已创建)

以此类推,如下图所示,创建了2个核心线程,执行2个任务,假设执行时间会很长,新来的2个任务进入阻塞队列。

如果再来1个任务,注意,线程池不会有拒绝策略,此时会创建一个救急线程,由救急线程来执行。

救急线程执行完任务后,会生存一段时间(由keepAliveTime和unit共同设置)被销毁。核心线程执行完后,则继续保留。

还有一种情况,都满了,才会执行拒绝策略

大致如下图,

注意,因为提供了各种用途的线程池,有的线程池采用有界队列,如果是无界队列,那么就不会有救急线程,只会用核心线程轮询执行。


newFixedThreadPool
内部是无界队列;某个线程发生异常后而结束,那么会新建一个线程

示例代码:
@Slf4j(topic ="c.test")
public class Test38 {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.execute(()->{
log.debug("1");
});
pool.execute(()->{
log.debug("2");
});
pool.execute(()->{
log.debug("3");
});
}
}
输出如下:有2个线程,线程ID从1开始,任务1、任务2开始执行,然后任务3放在队列等待执行。
20:24:33.409 [pool-1-thread-2] DEBUG c.test - 2
20:24:33.409 [pool-1-thread-1] DEBUG c.test - 1
20:24:33.414 [pool-1-thread-2] DEBUG c.test - 3
可以看到程序还未结束,核心线程还在运行。

SynchronousQueue
newSingleThreadExecutor
核心线程数和最大线程数都是1,没有救急线程,队列是阻塞的无界队列。该线程池作用,任务串行执行。

@Slf4j(topic ="c.test")
public class Test39 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(()->{
log.debug("1");
int a=1/0;
});
executorService.execute(()->{
log.debug("2");
});
executorService.execute(()->{
log.debug("3");
});
}
}
输出:失败后会新建一个线程2
21:05:00.801 [pool-1-thread-1] DEBUG c.test - 1
21:05:00.804 [pool-1-thread-2] DEBUG c.test - 2
21:05:00.804 [pool-1-thread-2] DEBUG c.test - 3
Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
at com.example.duoxiancheng.study15_jdk_xian_cheng_chi.Test39.lambda$main$0(Test39.java:17)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
线程池的submit方法
带结果返回的执行方法;
@Slf4j(topic = "c.test")
public class Test40 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService pool = Executors.newFixedThreadPool(2);
//new Callable的泛型是返回类型,Future的泛型也是
Future<String> future = pool.submit(new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(1000);
return "okok";
}
});
//get方法会阻塞等待结果
log.debug("get:{}",future.get());
}
}
输出:阻塞了1秒,等待结果返回
21:19:40.863 [pool-1-thread-1] DEBUG c.test - run...
21:19:41.870 [main] DEBUG c.test - get:okok
程序不会停止,一直运行着

invokeAll方法

演示不带超时的方法
@Slf4j(topic = "c.test")
public class Test35 {
public static void main(String[] args) throws InterruptedException {
ExecutorService pool = Executors.newFixedThreadPool(2);
List<Future<String>> futures = pool.invokeAll(Arrays.asList(
() -> {
log.debug("run");
return "1";
},
() -> {
log.debug("run");
return "2";
},
() -> {
log.debug("run");
return "3";
}
));
futures.forEach(f->{
try {
log.debug("result:{}",f.get());
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
});
}
}
输出如下:注意,执行任务有先后顺序,先执行1、2,3排队
09:31:42.489 [pool-1-thread-1] c.test - run
09:31:42.489 [pool-1-thread-2] c.test - run
09:31:42.489 [pool-1-thread-2] c.test - run
09:31:42.489 [main] c.test - result:1
09:31:42.499 [main] c.test - result:2
09:31:42.499 [main] c.test - result:3
invokeAny

3个线程,3个任务同时执行

如果只有1个线程,那么输出的是任务1的结果。因为只有1个线程,任务2、任务3在排队,要等任务1执行完。

输出如下:只会输出任务1的(完整)结果,任务2只打印了“begin 2”,就运行了一瞬,就结束了。

线程池停止
异步模式之工作线程
定义
让有限的工作线程来轮流异步处理无限多的任务。它的典型实现是线程池,这也体现了享元模式。
举例说明
例如,餐厅服务员(线程)是有限的,轮流处理客人的点餐(任务)。它的反面是为每个客人准备一个服务员,对应另一种设计模式,Thread-Per-Message

饥饿

演示饥饿
服务一个客人时,不会有饥饿现象
@Slf4j(topic = "c.test")
public class Test40 {
static final List<String> MENU = Arrays.asList("白切鸡","烧鹅","叉烧","蒸排骨","炒牛河");
static Random random =new Random();
static String cooking(){
return MENU.get(random.nextInt(MENU.size()));
}
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.execute(() -> {
log.debug("处理点餐");
Future<String> f = pool.submit(() -> {
log.debug("做菜");
return cooking();
});
try {
log.debug("上菜:{}",f.get());
} catch (InterruptedException |ExecutionException e) {
throw new RuntimeException(e);
}
});
}
}
输出:
08:37:23.159 [pool-1-thread-1] c.test - 处理点餐
08:37:23.164 [pool-1-thread-2] c.test - 做菜
08:37:23.164 [pool-1-thread-1] c.test - 上菜:炒牛河
服务两个客人时,会有饥饿现象
@Slf4j(topic = "c.test")
public class Test40 {
static final List<String> MENU = Arrays.asList("白切鸡","烧鹅","叉烧","蒸排骨","炒牛河");
static Random random =new Random();
static String cooking(){
return MENU.get(random.nextInt(MENU.size()));
}
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.execute(() -> {
log.debug("处理点餐");
Future<String> f = pool.submit(() -> {
log.debug("做菜");
return cooking();
});
try {
log.debug("上菜:{}",f.get());
} catch (InterruptedException |ExecutionException e) {
throw new RuntimeException(e);
}
});
pool.execute(() -> {
log.debug("处理点餐");
Future<String> f = pool.submit(() -> {
log.debug("做菜");
return cooking();
});
try {
log.debug("上菜:{}",f.get());
} catch (InterruptedException |ExecutionException e) {
throw new RuntimeException(e);
}
});
}
}
结果如下:只有两个线程,两个线程都在运行(等待结果)

解决方案
将线程数设置为3,治标不治本,如果来了更多客人就不行了。
ExecutorService pool = Executors.newFixedThreadPool(3);
采用工作线程模式
设置两个线程池,一个负责点餐,一个负责做菜。
@Slf4j(topic = "c.test")
public class Test40 {
static final List<String> MENU = Arrays.asList("白切鸡","烧鹅","叉烧","蒸排骨","炒牛河");
static Random random =new Random();
static String cooking(){
return MENU.get(random.nextInt(MENU.size()));
}
public static void main(String[] args) {
//服务员线程池,处理点餐
ExecutorService waiterPool = Executors.newFixedThreadPool(1);
//厨师线程池,处理做菜
ExecutorService cookPool = Executors.newFixedThreadPool(1);
waiterPool.execute(() -> {
log.debug("处理点餐");
Future<String> f = cookPool.submit(() -> {
log.debug("做菜");
return cooking();
});
try {
log.debug("上菜:{}",f.get());
} catch (InterruptedException |ExecutionException e) {
throw new RuntimeException(e);
}
});
waiterPool.execute(() -> {
log.debug("处理点餐");
Future<String> f = cookPool.submit(() -> {
log.debug("做菜");
return cooking();
});
try {
log.debug("上菜:{}",f.get());
} catch (InterruptedException |ExecutionException e) {
throw new RuntimeException(e);
}
});
}
}
输出如下:
08:50:57.384 [pool-1-thread-1] c.test - 处理点餐
08:50:57.384 [pool-2-thread-1] c.test - 做菜
08:50:57.384 [pool-1-thread-1] c.test - 上菜:炒牛河
08:50:57.384 [pool-1-thread-1] c.test - 处理点餐
08:50:57.384 [pool-2-thread-1] c.test - 做菜
08:50:57.384 [pool-1-thread-1] c.test - 上菜:炒牛河
线程池设置多少个线程合适
任务调度线程池
Timer
不推荐使用

示例讲解
1秒后执行

1秒后应该同时执行任务1和任务2,但是由于任务1,执行了2秒,导致任务2延迟2秒。

未处理异常,直接抛了异常,导致任务2没有执行到。

ScheduledThreadPoolExecutor
能同时执行

延迟执行
@Slf4j(topic = "c.test")
public class Test36 {
public static void main(String[] args) {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
log.debug("start....");
pool.scheduleAtFixedRate(
() -> {
log.debug("running...");
}
,2,1, TimeUnit.SECONDS);//延迟2秒,每隔1秒
}
}
输出:
10:47:40.577 [main] c.test - start....
10:47:42.615 [pool-1-thread-1] c.test - running...
10:47:43.617 [pool-1-thread-1] c.test - running...
10:47:44.620 [pool-1-thread-2] c.test - running...
10:47:45.621 [pool-1-thread-1] c.test - running...
10:47:46.613 [pool-1-thread-1] c.test - running...
如果任务执行得比较长,那么会推迟执行
@Slf4j(topic = "c.test")
public class Test36 {
public static void main(String[] args) {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
log.debug("start....");
pool.scheduleAtFixedRate(
() -> {
log.debug("running...");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
,2,1, TimeUnit.SECONDS);//延迟2秒,每隔1秒
}
}
输出如下:模拟任务执行3秒,变成了每3秒执行
10:49:30.021 [main] c.test - start....
10:49:32.057 [pool-1-thread-1] c.test - running...
10:49:35.074 [pool-1-thread-1] c.test - running...
10:49:38.080 [pool-1-thread-2] c.test - running...
10:49:41.092 [pool-1-thread-2] c.test - running...
10:49:44.104 [pool-1-thread-2] c.test - running...
10:49:47.106 [pool-1-thread-2] c.test - running...
scheduleWithFixedDelay
每执行完一个任务后间隔固定的一段时间运行

线程池之处理异常
1.自己try catch处理

2.使用submit、future,这种带返回值的方式,没有异常的话就会返回true,有异常则抛出来

如果没处理异常,程序则不会输出任何信息。
线程应用之定时任务
tomcat线程池讲解
看源码时再研究
浙公网安备 33010602011771号