ThreadPool线程池

目录

1、参数说明 

2、线程池运行流程

3、线程池状态  

4、newFixedThreadPool

5、newCachedThreadPool

6、 newSingleThreadExecutor

7、提交任务        

7、关闭线程池


1、参数说明 

下面是JDK线程池最全参数的构造方法,所有的Executors构造的线程池都是基于该方法,只不过给你封装了很多参数,简化了,你拿着就用。

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

corePoolSize: 核心线程数

maximumPoolSize: 最大线程数

keepAliveTime: 生存时间--针对救急线程

unit: 时间单位--针对救急线程

workQueue: 阻塞队列

    -SynchronousQueue    :同步阻塞队列

    -LinkedBlockingQueue :基于链表的无界阻塞队列

    -ArrayBlockingQueue   :基于数组的有界阻塞队列

threadFactory: 线程工厂--便于管理,如:起个方便日志跟踪定位的名称

handle: 据决策略 

    -AbortPolicy:默认策略,让调用者抛出RejectedExecutionException异常

    -CallerRunsPolicy:让调用者自己运行任务

    -DiscardPolicy:放弃本次任务

    -DiscardOldestPolicy:放弃队列中最早的任务,本任务取而代之

2、线程池运行流程

 

3、线程池状态  

 ThreadPoolExecutor使用int的高3位来表示线程池状态,低29位表示线程数量

状态名高3位接受新任务处理阻塞队列任务说明
RUNNING111YY011>010>001>000>111(第一位表示负数)
SHUTDOWN000NY不会接受新任务,但会处理阻塞队列剩余任务
STOP001NN会中断正在执行的任务,并抛弃阻塞队列任务
TIDYING010任务全执行完毕,活动线程为0即将进入终结
TERMINATED011

终结状态

4、newFixedThreadPool

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

特点:

核心线程数 == 最大线程数(没有救急线程被创建),因此无需超时时间 0L

阻塞队列是无界的,可以放任意数量的任务

使用场景:适用于任务量已知,相对耗时的任务(长连接) 

5、newCachedThreadPool

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

特点:

核心线程数是0,最大线程数是Integer.MAX_VALUE,救急线程的空闲时间是60s,意味着

        -全部都是救急线程(60s后可回收)

        -救急线程可以"无限"创建(Integer.MAX_VALUE)

队列采用SynchronousQueue实现特点是,它没有容量,没有线程来取是放不进去的(类似生活中:一手交钱,一手交货)

SynchronousQueue:举个栗子,下面有本项目搭建展示

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.SynchronousQueue;

/**
 * @Author George
 * @Date 2022/11/11 6:44
 * @VDesc
 */
@Slf4j
public class TestExecutors {

    public static void main(String[] args) {

        SynchronousQueue<Integer> syncQueue = new SynchronousQueue<>();

        new Thread(() -> {
            try {
                log.debug("putting {}",1);
                syncQueue.put(1);
                log.debug("put end {}",1);

                log.debug("putting {}",2);
                syncQueue.put(2);
                log.debug("put end {}",2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        },"t1").start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            try {
                log.debug("taking {}",1);
                syncQueue.take();

            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        },"t2").start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            try {
                log.debug("taking {}",2);
                syncQueue.take();

            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        },"t3").start();

    }

输出:

08:51:19.613 [t1] com.example.threadpool.TestExecutors - putting 1
08:51:20.616 [t2] com.example.threadpool.TestExecutors - taking 1
08:51:20.617 [t1] com.example.threadpool.TestExecutors - put end 1
08:51:20.618 [t1] com.example.threadpool.TestExecutors - putting 2
08:51:21.616 [t3] com.example.threadpool.TestExecutors - taking 2
08:51:21.616 [t1] com.example.threadpool.TestExecutors - put end 2 

说明:

可以看到t1往任务队列放任务的时放不进去,1s后,当t2来取的任务时候它才 put end;接着t1放任务2,还是要在等1s后,线程t3来取任务时,才能 put end 2 

使用场景:整个线程池表现为线程数会根据任务量不断增长,没有上限,当任务执行完毕,空闲1分钟后释放线程。

适合任务数比较密集,但每个任务执行时间较短的情况(短连接)

项目搭建:

 创建项目只需要导入2个依赖和一个log日志打印模板

<!--   lombok     -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>

<!--   log     -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.jetbrains</groupId>
            <artifactId>annotations</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://ch.qos.logback/xml/ns/logback"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback logback.xsd">
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%date{HH:mm:ss.SSS} [%t] %logger - %m%n</pattern>
        </encoder>
    </appender>

    <logger name="com.example" level="debug" additivity="false">
        <appender-ref ref="STDOUT"/>
    </logger>

    <root level="ERROR">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

6、 newSingleThreadExecutor

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

 使用场景:

希望多个任务排队执行(串行)。线程数固定为1,任务数多于1时,会放入无界队列排队。任务执行完毕,这唯一的线程也不会被释放。

比较区别:

        -自己创建一个单线程串行执行任务,如果任务执行失败而终止,那么没有任何补救措施;而线程池还会新建一个线程,保证池的正常工作。

    private static void testSingleExecutor() {
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

        singleThreadExecutor.execute(() -> {
            log.debug("任务1");
            int i = 1 / 0;      // ArithmeticException
        });

        singleThreadExecutor.execute(() -> {
            log.debug("任务2");
        });
        singleThreadExecutor.execute(() -> {
            log.debug("任务3");
        });
    }

 可以导入上面的TestExecutors类中执行

09:09:38.815 [pool-1-thread-1] com.example.threadpool.TestExecutors - 任务1
09:09:38.825 [pool-1-thread-2] com.example.threadpool.TestExecutors - 任务2
09:09:38.826 [pool-1-thread-2] com.example.threadpool.TestExecutors - 任务3
Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
    at com.example.threadpool.TestExecutors.lambda$main$0(TestExecutors.java:22)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

说明:

可以看到线程池创建的第一个线程由于异常结束了,但是任务2、3没有因为[pool-1-thread-1] 线程1中断而终止下面任务的执行。

-Executors.newSingleThreadExecutor()线程数始终为1,不能修改 

        -FinalizableDelegatedExecutorService应用的是装饰器模式,只对外暴露了ExecutorService接口,因此不能调用ThreadPoolExecutor中特有的方法

-Executors.newFixedThreadPool(1)初始时为1,以后可以修改

        -对外暴露的是ThreaPoolExecutor对象,可以强转后调用setCorePoolSize等方法经行修改

7、提交任务        

// 执行任务

void executor()

// 提交任务task,返回值Future 获得任务执行结果

<T> Future<T> submit(Callable<T> task);

// 提交 task 中所有任务

<T>  List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)

                        throw InterruptedException;

// 提交 tasks 中所有任务,带超时时间

<T>  List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,long timeout,                                             TimeUnit unit)  throw InterruptedException;

// 提交 tasks中所有任务,那个任务先执行成功执行完毕,返回任务执行结果,其他任务取消

<T>  T invokeAny(Collection<? extends Callable<T>> tasks)

                        throw InterruptedException, ExecutionException;

// 提交 tasks中所有任务,那个任务先执行成功执行完毕,返回任务执行结果,其他任务取消,带超时时间

<T>  T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)

                        throw InterruptedException, ExecutionException, TimeoutException;

testSubmit()

    private static void testSubmit() throws InterruptedException, ExecutionException {
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        Future<String> future = executorService.submit(() -> {
            try {
                log.debug("executor running....");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "ok";
        });

        log.debug("主线程接收线程池返回对象{}",future.get());
    }

 testInvokeAll()

private static void testInvokeAll() throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        List<Future<String>> futures = executorService.invokeAll(Arrays.asList(
                () -> {
                    log.debug("begin 1");
                    Thread.sleep(1000);
                    return "1";
                },
                () -> {
                    log.debug("begin 2");
                    Thread.sleep(500);
                    return "2";
                },
                () -> {
                    log.debug("begin 3");
                    Thread.sleep(2000);
                    return "3";
                }
        ));

testInvokeAny() 

private static void testInvokeAny() throws InterruptedException, ExecutionException {
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        // 核心线程从 2 -> 1,收到的最终结果1,因为其他任务会放入队列,等1已经有结果了,invokeAny就返回了
        String res = executorService.invokeAny(Arrays.asList(
                () -> {
                    log.debug("begin 1");
                    Thread.sleep(1000);
                    log.debug("end 1");
                    return "1";
                },
                () -> {
                    log.debug("begin 2");
                    Thread.sleep(500);
                    log.debug("end 2");
                    return "2";
                },
                () -> {
                    log.debug("begin 3");
                    Thread.sleep(2000);
                    log.debug("end 3");
                    return "3";
                }
        ));

        log.debug("收到的最终结果{}",res);
    }

7、关闭线程池

shutdown 

线程池状态变为 SHUTDOWN;不会接受新任务;但已提交任务会执行完;此方法不会阻塞调用线程的执行。

    public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            // 修改线程池状态 RUNNING -> SHUTDOWN
            advanceRunState(SHUTDOWN);
            // 仅会打断空闲线程
            interruptIdleWorkers();
            onShutdown(); // 扩展点,给子类用的 ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        // 尝试终结(没有可运行的线程,可以立刻终结,如果还有运行的线程也不会等,没有运行完的线程让它自己运行完自己去结束)
        tryTerminate();
    }

shutdownNow

线程池状态变为 STOP ;不会接受新任务,会将队列中的任务返回;并用interrupt的方式终断正在执行的任务

    public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            // 修改线程状态
            advanceRunState(STOP);
            // 打断所有线程
            interruptWorkers();
            // 获取队列中剩余任务
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        // 尝试终结
        tryTerminate();
        return tasks;
    }

 其他方法

// 不在RUNNING 状态的线程池,此方法就返回 true

boolean isShutdown();

// 线程池状态是否是 TERMINATED

boolean isTerminated();

// 调用 shutdown 后,由于调用线程并不会等待所有任务运行结束,因此如果想在线程池 TERMINATED 后做些事情,可以利用此方法等待

boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException

举个栗子:
 shutdown()方法测试

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Author George
 * @Date 2022/11/12 22:28
 * @VDesc
 */
@Slf4j
public class TestShutDown {

    public static void main(String[] args) {

        testShutDown();
    }

    private static void testShutDown() {
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        executorService.submit(() -> {
            log.debug("task 1 running.....");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug("task 1 finish.....");
            return 1;
        });

        executorService.submit(() -> {
            log.debug("task 2 running.....");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug("task 2 finish.....");
            return 2;
        });

        executorService.submit(() -> {
            log.debug("task 3 running.....");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug("task 3 finish.....");
            return 3;
        });

        // 第1步,已提交(线程运行的,阻塞队列中的)任务会执行完,不会阻塞调用线程的执行
        log.debug("shutdown");
        executorService.shutdown();
        log.debug("run other....");

        // 第2步,调用shutdown方法之后,再去提交任务会抛异常
        executorService.submit(() -> {
            log.debug("task 4 running.....");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug("task 4 finish.....");
            return 4;
        });
    }

}

22:37:05.058 [main] com.example.threadpool.TestShutDown - shutdown
22:37:05.063 [main] com.example.threadpool.TestShutDown - run other....
22:37:05.058 [pool-1-thread-1] com.example.threadpool.TestShutDown - task 1 running.....
22:37:05.058 [pool-1-thread-2] com.example.threadpool.TestShutDown - task 2 running.....
22:37:06.121 [pool-1-thread-2] com.example.threadpool.TestShutDown - task 2 finish.....
22:37:06.121 [pool-1-thread-1] com.example.threadpool.TestShutDown - task 1 finish.....
22:37:06.121 [pool-1-thread-2] com.example.threadpool.TestShutDown - task 3 running.....
22:37:07.135 [pool-1-thread-2] com.example.threadpool.TestShutDown - task 3 finish.....

说明:

已提交(正在运行的线程【1、2线程】、阻塞队列中的【3线程】)任务会执行完毕,不会阻塞调用线程的执行(也就是main线程),main执行完shutdown方法之后,马上执行了run other(可以看到没有阻塞)

 shutdownNow()方法测试

   private static void testShutDownNow() {
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        executorService.submit(() -> {
            log.debug("task 1 running.....");
            Thread.sleep(1000);
            log.debug("task 1 finish.....");
            return 1;
        });

        executorService.submit(() -> {
            log.debug("task 2 running.....");
            Thread.sleep(1000);
            log.debug("task 2 finish.....");
            return 2;
        });

        executorService.submit(() -> {
            log.debug("task 3 running.....");
            Thread.sleep(1000);
            log.debug("task 3 finish.....");
            return 3;
        });

        // 已提交(线程运行的,阻塞队列中的)任务会被打断,终止执行
        log.debug("shutdownNow");
        List<Runnable> runnables = executorService.shutdownNow();
        log.debug("run other.... {}",runnables);

    }

22:46:18.385 [main] com.example.threadpool.TestShutDown - shutdown
22:46:18.388 [pool-1-thread-2] com.example.threadpool.TestShutDown - task 2 running.....
22:46:18.386 [pool-1-thread-1] com.example.threadpool.TestShutDown - task 1 running.....
22:46:18.391 [main] com.example.threadpool.TestShutDown - run other.... [java.util.concurrent.FutureTask@2a18f23c] 

说明:

可以看到1、2线程随着shutdownNow的执行,2个都终止运行了;线程3提交到阻塞队列的任务是可以返回接收的

白首为功名,旧山松竹老,阻归程。欲将心事付瑶琴,知音少,弦断有谁听? 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值