JAVA - 并发 - 线程池

线程池

自定义线程池

1.降低资源消耗‌:通过复用已创建的线程,避免频繁地创建和销毁线程所带来的开销。线程的创建与销毁涉及操作系统层面的操作,如分配栈空间、初始化上下文等,这些操作在高并发场景下会显著消耗系统资源。

‌2.提高响应速度‌:当任务到达时,可以直接从线程池中获取一个空闲线程来执行任务,而无需等待新线程的创建过程,从而加快了任务的响应速度。

实现

Thread pool是线程池,Blocking Queue是任务阻塞队列,主线程、t1、t2生产任务,线程池消费任务
image
示例代码:之前的疑问,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);
            });
        }

    }

image

输出如下:

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个任务等待加入任务队列
image
image
输出如下,第13个任务一直在等着进入任务队列
image
解决方法,新增带超时的队列添加的方法

    //带超时的阻塞添加
    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

image

线程池状态

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 - - 终止状态

image

ThreadPoolExecutor的相关参数说明

核心线程数,线程池创建时设置的核心线程数目。
最大线程,救急线程数 + 核心线程
image

如下图所示,虚线表示线程还未创建,2个核心,1个救急,最大线程数目3。
image
现在有任务1要执行,创建了一个核心线程(实线表示已创建)
image
以此类推,如下图所示,创建了2个核心线程,执行2个任务,假设执行时间会很长,新来的2个任务进入阻塞队列。
image
如果再来1个任务,注意,线程池不会有拒绝策略,此时会创建一个救急线程,由救急线程来执行。
image
救急线程执行完任务后,会生存一段时间(由keepAliveTime和unit共同设置)被销毁。核心线程执行完后,则继续保留。
image
还有一种情况,都满了,才会执行拒绝策略
image
大致如下图,
image
注意,因为提供了各种用途的线程池,有的线程池采用有界队列,如果是无界队列,那么就不会有救急线程,只会用核心线程轮询执行。
image
image

newFixedThreadPool

内部是无界队列;某个线程发生异常后而结束,那么会新建一个线程
image
示例代码:

@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

可以看到程序还未结束,核心线程还在运行。
image

SynchronousQueue

newSingleThreadExecutor

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

@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

程序不会停止,一直运行着
image

invokeAll方法

image
演示不带超时的方法

@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

image
3个线程,3个任务同时执行
image
如果只有1个线程,那么输出的是任务1的结果。因为只有1个线程,任务2、任务3在排队,要等任务1执行完。
image
输出如下:只会输出任务1的(完整)结果,任务2只打印了“begin 2”,就运行了一瞬,就结束了。
image

线程池停止

异步模式之工作线程

定义

让有限的工作线程来轮流异步处理无限多的任务。它的典型实现是线程池,这也体现了享元模式。

举例说明

例如,餐厅服务员(线程)是有限的,轮流处理客人的点餐(任务)。它的反面是为每个客人准备一个服务员,对应另一种设计模式,Thread-Per-Message
image

饥饿

image

演示饥饿

服务一个客人时,不会有饥饿现象

@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);
            }
        });

    }
}

结果如下:只有两个线程,两个线程都在运行(等待结果)
image

解决方案

将线程数设置为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

不推荐使用
image
示例讲解
1秒后执行
image
1秒后应该同时执行任务1和任务2,但是由于任务1,执行了2秒,导致任务2延迟2秒。
image
未处理异常,直接抛了异常,导致任务2没有执行到。
image

ScheduledThreadPoolExecutor

能同时执行
image

延迟执行

@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

每执行完一个任务后间隔固定的一段时间运行
image

线程池之处理异常

1.自己try catch处理
image
2.使用submit、future,这种带返回值的方式,没有异常的话就会返回true,有异常则抛出来
image

如果没处理异常,程序则不会输出任何信息。

线程应用之定时任务

tomcat线程池讲解

看源码时再研究

Fork/Join

posted @ 2026-02-10 16:36  dvdhellohaha  阅读(15)  评论(0)    收藏  举报