精华内容
下载资源
问答
  • 线程池-创建销毁

    千次阅读 2019-01-05 10:56:15
    Windows提供了一个线程池机制来简化线程池创建销毁以及日常管理。调用线程池函数时,系统会为进程创建相应的内核资源,其中一些内核资源在进程终止之前都将一直存在。线程池的开销取决于用法:系统会以进程的...

    创建线程池

    Windows提供了一个线程池机制来简化线程池的创建、销毁以及日常管理。调用线程池函数时,系统会为进程创建相应的内核资源,其中一些内核资源在进程终止之前都将一直存在。线程池的开销取决于用法:系统会以进程的名义来分配线程、其他内核对象以及内部数据结构。

    创建一个新的线程池需调用函数:

    PTP_POOL CreateThreadpool(PVOID reserved);
    

    其中reserved是保留的,应该传入参数为NULL。返回值为创建的线程池值。

    为了更好的利用计算机的性能,可以通过系统函数获取到计算机的参数:

    创建好线程池之后就可以进行设置线程数量。

    以异步方式调用函数

    为执行线程池异步调用函数,需要使用下面函数:

    VOID NTAPI SimpleCallback(PTP_CALLBACK_INSTANCE pInstance, PVOID pvContext);
    

    为了让线程池中的一个线程执行该函数,需要向线程池提交一个请求。

    BOOL TrySubThreadpoolCallback(PTP_SIMPLE_CALLBACK pfnCallback, PVOID pvContext, PTP_CALLBAACK_ENVIRON pcbe);
    

    该函数通过将一个工作项添加到线程池的队列中,若调用成功,则返回TRUE,调用失败,则返回FALSE。

    创建工作项时需要调用下面函数:

    VOID CALLBACK WorkCallback(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_WORK Work);
    

    向线程池提交请求时,可以使用函数:

    VOID SubminThreadpoolWork(PTP_WORK pWork);
    

    如果存在另一个线程想要取消已经提交的工作项,或该线程由于要等待工作项处理完毕而需要将自己挂起,可以使用下面的函数:

    VOID WaitForTreadpoolWorkCallbacks(PTP_WORK pWork, BOOL bCancelPendingCallbacks);
    

    不再需要一个工作项时,可以调用下面的函数:

    VOID CloseThreadpoolWork(PTP_WORK pwk);
    

    不需要自己调用CreateThread。系统会自动为我们创建一个默认的线程池,并让线程池中的一个线程来调用我们的回调函数。当线程处理完一个请求之后,他不会立刻被销毁,而是会回到线程池中,准备处理队列中的任何其他工作项。线程池会不断地重复使用其中的线程,而不会频繁的创建和销毁线程。

    每隔一段时间调用一个函数

    每个一段时间进行调用一个函数相当于程序执行任务是基于一个计时器进行执行。如果使用一个可等待的计时器对象会浪费系统资源,只需要创建一个可等待计时器即可。

    在内核对象触发时调用一个函数

    内核对象触发的时机,请参考如下:
    内核对象触发

    在异步I/O请求完成时调用一个函数

    销毁线程池

    当程序需要销毁线程池时,调用下面的函数:

    VOID CloseThreadpoolGroupMembers(PTP_CLEANUP_GROUP ptpcg, BOOL bCanclePendingCallbacks, PVOID pvCleanupContext);
    

    当bCanclePendingCallbacks为true时,函数会将已经提交但尚未处理的工作项直接取消。

    当所有的工作项被取消或处理之后,调用下面的函数来释放清理组所占用的资源:

    VOID WINAPI CloseThreadpoolCleanupGroup(PTP_CLEANUP_GROUP ptpcg);
    

    最后可以调用DestroyThreadpoolEnvironment和CloseThreadpool,这样就完全关闭线程池。

    展开全文
  • 封装好的线程池源码,直接可以放到工程中使用,自动管理线程的创建和销毁
  • 线程池创建

    千次阅读 2019-03-04 16:22:40
    Executors线程池创建,主要包含以下几种方式: 1、第一种: /** * 测试: 提交15个执行时间需要3秒的任务,看线程池的状况 * * @param threadPoolExecutor 传入不同的线程池,看不同的结果 * @throws Exception *...

    Executors线程池创建,主要包含以下几种方式:

    1、第一种:

    /**
     * 测试: 提交15个执行时间需要3秒的任务,看线程池的状况
     *
     * @param threadPoolExecutor 传入不同的线程池,看不同的结果
     * @throws Exception
     */
    public void testCommon(ThreadPoolExecutor threadPoolExecutor) throws Exception {
        // 测试: 提交15个执行时间需要3秒的任务,看超过大小的2个,对应的处理情况
        for (int i = 0; i < 15; i++) {
            int n = i;
            threadPoolExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("开始执行:" + n);
                        Thread.sleep(3000L);
                        System.err.println("执行结束:" + n);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            System.out.println("任务提交成功 :" + i);
        }
        // 查看线程数量,查看队列等待数量
        Thread.sleep(500L);
        System.out.println("当前线程池线程数量为:" + threadPoolExecutor.getPoolSize());
        System.out.println("当前线程池等待的数量为:" + threadPoolExecutor.getQueue().size());
        // 等待15秒,查看线程数量和队列数量(理论上,会被超出核心线程数量的线程自动销毁)
        Thread.sleep(15000L);
        System.out.println("当前线程池线程数量为:" + threadPoolExecutor.getPoolSize());
        System.out.println("当前线程池等待的数量为:" + threadPoolExecutor.getQueue().size());
    }
    
    /**
     * 1、线程池信息: 核心线程数量5,最大数量10,无界队列,超出核心线程数量的线程存活时间:5秒, 指定拒绝策略的线程
     *
     * @throws Exception
     */
    private void threadPoolExecutorTest1() throws Exception {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>());
        testCommon(threadPoolExecutor);
    }
    

    通过Executors.newFixedThreadPool()创建:
    在这里插入图片描述
    ExecutorService executorService = Executors.newFixedThreadPool(5);
    以上两种方式都可以,只是Executors提供了相应的封装,简化了部分过程。

    通过执行结果分析所得:
    核心线程数为5,最大线程数不起作用,超出线程会在LinkedBlockingQueue队列中等待。每次执行会从队列中取出任务执行。

    第二种:
    /**
    * 2、 线程池信息: 核心线程数量5,最大数量10,队列大小3,超出核心线程数量的线程存活时间:5秒, 指定拒绝策略的
    *
    * @throws Exception
    */
    private void threadPoolExecutorTest2() throws Exception {
    // 创建一个 核心线程数量为5,最大数量为10,等待队列最大是3 的线程池,也就是最大容纳13个任务。
    // 默认的策略是抛出RejectedExecutionException异常,java.util.concurrent.ThreadPoolExecutor.AbortPolicy
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS,
    new LinkedBlockingQueue(3), new RejectedExecutionHandler() {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
    System.err.println(“有任务被拒绝执行了”);
    }
    });
    testCommon(threadPoolExecutor);
    // 预计结果:
    // 1、 5个任务直接分配线程开始执行
    // 2、 3个任务进入等待队列
    // 3、 队列不够用,临时加开5个线程来执行任务(5秒没活干就销毁)
    // 4、 队列和线程池都满了,剩下2个任务,没资源了,被拒绝执行。
    // 5、 任务执行,5秒后,如果无任务可执行,销毁临时创建的5个线程
    }
    执行结果:
    在这里插入图片描述
    初始5个线程执行,如果线程不够再开启5个线程(因为最大线程只有10个),如果超过10个线程,则放入队列中,这里的队列只能放3个任务,总共有15个线程,多余2个则不执行,通过拒绝策略类可以打印查看结果。

    第三种:
    // Executors工具类创建
    // ExecutorService executorService = Executors.newCachedThreadPool();

        // 和Executors.newCachedThreadPool()一样的
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>());
        testCommon(threadPoolExecutor);
        // 预计结果:
        // 1、 线程池线程数量为:15,超出数量的任务,其他的进入队列中等待被执行
        // 2、 所有任务执行结束,60秒后,如果无任务可执行,所有线程全部被销毁,池的大小恢复为0
    

    该线程处理一些不能预期到底需要创建几个线程,通过数据大小来自己创建,最大为 Integer.MAX_VALUE,但是不建议这样使用,如果线程太多,将造成执行卡屯严重,影响体验。

    第四种:
    /**
    * 5、 定时执行线程池信息:3秒后执行,一次性任务,到点就执行

    * 核心线程数量5,最大数量Integer.MAX_VALUE,DelayedWorkQueue延时队列,超出核心线程数量的线程存活时间:0秒
    *
    * @throws Exception
    */
    private void threadPoolExecutorTest5() throws Exception {
    // 和Executors.newScheduledThreadPool()一样的
    ScheduledThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(5);
    threadPoolExecutor.schedule(new Runnable() {
    @Override
    public void run() {
    System.out.println(“任务被执行,现在时间:” + System.currentTimeMillis());
    }
    }, 3000, TimeUnit.MILLISECONDS);
    System.out.println(
    “定时任务,提交成功,时间是:” + System.currentTimeMillis() + “, 当前线程池中线程数量:” + threadPoolExecutor.getPoolSize());
    // 预计结果:任务在3秒后被执行一次
    }

    执行结果:
    在这里插入图片描述
    该任务到点后执行一次后将不在执行,可以通过标示类控制,执行一次后关闭该任务。

    第五种:周期性任务
    ScheduledThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(5);
    threadPoolExecutor.scheduleAtFixedRate(new Runnable() {
    @Override
    public void run() {
    try {
    Thread.sleep(3000L);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println(“任务-1 被执行,现在时间:” + System.currentTimeMillis());
    }
    }, 2000, 1000, TimeUnit.MILLISECONDS);
    该任务执行过程:启动后延时2s, 间隔1s执行一次,执行一次需要休息3s, 也就是说当该任务在休息3s时,已经有3个任务需要执行了,这些任务在队列中等待,当第一个任务执行完成,立即执行在等待的任务,无需延时等待。

    //延时执行任务: 启动后延时2s, 间隔1s执行一次,执行一次需要休息3s,当任务执行完成后,按照上次任务执行完成的时间延时1s后,开始执行新的任务。例如:A任务启动执行完成时间为01s, 则下一个任务执行时间为02s开始。
    threadPoolExecutor.scheduleWithFixedDelay(new Runnable() {
    @Override
    public void run() {
    try {
    Thread.sleep(3000L);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println(“任务-2 被执行,现在时间:” + System.currentTimeMillis());
    }
    }, 2000, 1000, TimeUnit.MILLISECONDS);

    第六种:线程终止方式
    // 创建一个 核心线程数量为5,最大数量为10,等待队列最大是3 的线程池,也就是最大容纳13个任务。
    // 默认的策略是抛出RejectedExecutionException异常,java.util.concurrent.ThreadPoolExecutor.AbortPolicy
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS,
    new LinkedBlockingQueue(3), new RejectedExecutionHandler() {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
    System.err.println(“有任务被拒绝执行了”);
    }
    });
    testCommon(threadPoolExecutor);
    // 测试: 提交15个执行时间需要3秒的任务,看超过大小的2个,对应的处理情况
    // 1秒后终止线程池
    Thread.sleep(1000L);
    threadPoolExecutor.shutdown();
    // 再次提交提示失败
    threadPoolExecutor.submit(new Runnable() {
    @Override
    public void run() {
    System.out.println(“追加一个任务”);
    }
    });
    执行结果分析:
    // 1、 10个任务被执行,3个任务进入队列等待,2个任务被拒绝执行
    // 2、调用shutdown后,不接收新的任务,等待13任务执行结束,2个任务被拒绝执行。
    // 3、 追加的任务在线程池关闭后,无法再提交,会被拒绝执行

    /**
     *  立刻终止线程:线程池信息: 核心线程数量5,最大数量10,队列大小3,超出核心线程数量的线程存活时间:5秒, 指定拒绝策略的
     *
     * @throws Exception
     */
    private void threadPoolExecutorTest8() throws Exception {
        // 创建一个 核心线程数量为5,最大数量为10,等待队列最大是3 的线程池,也就是最大容纳13个任务。
        // 默认的策略是抛出RejectedExecutionException异常,java.util.concurrent.ThreadPoolExecutor.AbortPolicy
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(3), new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                System.err.println("有任务被拒绝执行了");
            }
        });
        // 测试: 提交15个执行时间需要3秒的任务,看超过大小的2个,对应的处理情况
       testCommon(threadPoolExecutor);
        // 1秒后终止线程池
        Thread.sleep(1000L);
        List<Runnable> shutdownNow = threadPoolExecutor.shutdownNow();
        // 再次提交提示失败
        threadPoolExecutor.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("追加一个任务");
            }
        });
        System.out.println("未结束的任务有:" + shutdownNow.size());
    
        // 结果分析
        // 1、 10个任务被执行,3个任务进入队列等待,2个任务被拒绝执行
        // 2、调用shutdownnow后,队列中的3个线程不再执行,10个线程被终止后抛出interrupted异常,2个任务被拒绝执行
        // 3、 追加的任务在线程池关闭后,无法再提交,会被拒绝执行
    }
    
    展开全文
  • 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗; 提高响应速度:当任务到达时,任务可以不需要的等到线程创建就能立即执行。 提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会...

    多线程与高并发目录:

    【多线程与高并发】一、什么是线程?什么是进程?线程与进程的区别是什么?

    【多线程与高并发】二、并发和并行的区别是什么?

    【多线程与高并发】三、线程的优先级是怎么回事

    【多线程与高并发】四、Java守护线程是什么

    【多线程与高并发】五、线程的生命周期

    【多线程与高并发】六、如何用线程池创建线程?Java线程池创建线程详解

    【多线程与高并发】七、ThreadLocal关键字

    【多线程与高并发】六、线程池

    线程池的主要思想是为了减少每次获取资源的消耗,提高对资源的利用率;

    • 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗;
    • 提高响应速度:当任务到达时,任务可以不需要的等到线程创建就能立即执行。
    • 提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控

    1.从Java源码开始

    ThreadPollExecutor起源是Executor接口开始的。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p4aALPA8-1608353586372)(D:\software\typora\workplace\imgs_thread\11.png)]

    之后在IDEA中快捷键ctrl+shift+alt+u,或者右击选择show diagram,也可以打开如下所示的包依赖图。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NCNWCVg6-1608353586376)(D:\software\typora\workplace\imgs_thread\12.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZB6DHxtb-1608353586377)(D:\software\typora\workplace\imgs_thread\13.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XMHCZVrn-1608353586380)(D:\software\typora\workplace\imgs_thread\14.png)]

    2.如何创建线程池?

    线程池的创建主要是通过两个类:Executors和ThreadPollExecutor。通常建议入门使用ThreadPollExecutor的方式来创建线程,这样可以更加明确线程池的运行规则,避免资源耗尽的风险。阿里巴巴手册不建议用Executors这种方式来创建。

    方法一、通过ThreadPollExecutor的方式

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YNSyL6I5-1608353586383)(D:\software\typora\workplace\imgs_thread\15.png)]

    方法二、通过Executor框架的工具类Executors来创建

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8GjnrPOG-1608353586384)(D:\software\typora\workplace\imgs_thread\16.png)]

    Executors 返回线程池对象的弊端如下:

    • FixedThreadPool 和 SingleThreadExecutor : 允许请求的队列长度为 Integer.MAX_VALUE ,可能堆积大量的请求,从而导致 OOM。
    • CachedThreadPool 和 ScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致 OOM。

    3.线程池的基本原理

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y0dyCeu0-1608353586385)(D:\software\typora\workplace\imgs_thread\17.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zqPVC5Mx-1608353586386)(D:\software\typora\workplace\imgs_thread\18.png)]

    4.ThreadPoolExecutor七大核心属性

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6WKqdhw8-1608353586386)(D:\software\typora\workplace\imgs_thread\19.png)]

    ThreadPoolExecutor 3 个最重要的参数:

    • corePoolSize : 核心线程数线程数定义了最小可以同时运行的线程数量。
    • maximumPoolSize : 当队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数。
    • workQueue: 当新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,新任务就会被存放在队列中。

    hreadPoolExecutor其他常见参数:

    1. keepAliveTime:当线程池中的线程数量大于 corePoolSize 的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 keepAliveTime才会被回收销毁;

    2. unit : keepAliveTime 参数的时间单位。

    3. threadFactory :executor 创建新线程的时候会用到。

    4. handler :饱和策略

      如果当前同时运行的线程数量达到最大线程数量并且队列也已经被放满了任时,ThreadPoolTaskExecutor 定义一些策略:

      • ThreadPoolExecutor.AbortPolicy:抛出 RejectedExecutionException来拒绝新任务的处理。
      • ThreadPoolExecutor.CallerRunsPolicy:调用执行自己的线程运行任务,也就是直接在调用execute方法的线程中运行(run)被拒绝的任务,如果执行程序已关闭,则会丢弃该任务。因此这种策略会降低对于新任务提交速度,影响程序的整体性能。如果您的应用程序可以承受此延迟并且你要求任何一个任务请求都要被执行的话,你可以选择这个策略。
      • ThreadPoolExecutor.DiscardPolicy 不处理新任务,直接丢弃掉。
      • ThreadPoolExecutor.DiscardOldestPolicy 此策略将丢弃最早的未处理的任务请求。

      举个例子: Spring 通过 ThreadPoolTaskExecutor 或者我们直接通过 ThreadPoolExecutor 的构造函数创建线程池的时候,当我们不指定 RejectedExecutionHandler 饱和策略的话来配置线程池的时候默认使用的是 ThreadPoolExecutor.AbortPolicy。在默认情况下,ThreadPoolExecutor 将抛出 RejectedExecutionException 来拒绝新来的任务 ,这代表你将丢失对这个任务的处理。 对于可伸缩的应用程序,建议使用 ThreadPoolExecutor.CallerRunsPolicy。当最大池被填满时,此策略为我们提供可伸缩队列。

    5.线程池的Demo

    package com.lcz.threadpool;
    
    import java.util.Date;
    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    // 创建一个runnable接口实现的类
    class MyRunnable implements Runnable{
    
        private String command;
    
        public MyRunnable(String s) {
            this.command = s;
        }
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " Start. Time = " + new Date());
            processCommand();
            System.out.println(Thread.currentThread().getName() + " End. Time = " + new Date());
        }
    
        private void processCommand() {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public String toString() {
            return this.command;
        }
    }
    
    public class ThreadPoolExecurorDemo {
    
        private static final int CORE_POOL_SIZE = 5;
        private static final int MAX_POOL_SIZE = 10;
        private static final int QUEUE_CAPACITY = 100;
        private static final Long KEEP_ALIVE_TIME = 1L;
    
        // 主函数
        public static void main(String[] args){
            //通过ThreadPoolExecutor构造函数自定义参数创建
            ThreadPoolExecutor executor = new ThreadPoolExecutor(
                    CORE_POOL_SIZE,
                    MAX_POOL_SIZE,
                    KEEP_ALIVE_TIME,
                    TimeUnit.SECONDS,
                    new ArrayBlockingQueue<>(QUEUE_CAPACITY),
                    new ThreadPoolExecutor.CallerRunsPolicy());
    
            for (int i = 0; i < 10; i++) {
                //创建WorkerThread对象(WorkerThread类实现了Runnable 接口)
                Runnable worker = new MyRunnable("" + i);
                //执行Runnable
                executor.execute(worker);
            }
            //终止线程池
            executor.shutdown();
            while (!executor.isTerminated()) {
            }
            System.out.println("Finished all threads");
        }
    }
    
    

    结果输出:

    pool-1-thread-4 Start. Time = Fri Dec 04 17:03:13 CST 2020
    pool-1-thread-2 Start. Time = Fri Dec 04 17:03:13 CST 2020
    pool-1-thread-1 Start. Time = Fri Dec 04 17:03:13 CST 2020
    pool-1-thread-5 Start. Time = Fri Dec 04 17:03:13 CST 2020
    pool-1-thread-3 Start. Time = Fri Dec 04 17:03:13 CST 2020
    pool-1-thread-2 End. Time = Fri Dec 04 17:03:18 CST 2020
    pool-1-thread-1 End. Time = Fri Dec 04 17:03:18 CST 2020
    pool-1-thread-4 End. Time = Fri Dec 04 17:03:18 CST 2020
    pool-1-thread-2 Start. Time = Fri Dec 04 17:03:18 CST 2020
    pool-1-thread-1 Start. Time = Fri Dec 04 17:03:18 CST 2020
    pool-1-thread-4 Start. Time = Fri Dec 04 17:03:18 CST 2020
    pool-1-thread-3 End. Time = Fri Dec 04 17:03:18 CST 2020
    pool-1-thread-5 End. Time = Fri Dec 04 17:03:18 CST 2020
    pool-1-thread-3 Start. Time = Fri Dec 04 17:03:18 CST 2020
    pool-1-thread-5 Start. Time = Fri Dec 04 17:03:18 CST 2020
    pool-1-thread-4 End. Time = Fri Dec 04 17:03:23 CST 2020
    pool-1-thread-1 End. Time = Fri Dec 04 17:03:23 CST 2020
    pool-1-thread-2 End. Time = Fri Dec 04 17:03:23 CST 2020
    pool-1-thread-3 End. Time = Fri Dec 04 17:03:23 CST 2020
    pool-1-thread-5 End. Time = Fri Dec 04 17:03:23 CST 2020
    Finished all threads
    
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1nZzPqL2-1608353586387)(D:\software\typora\workplace\imgs_thread\20.png)]

    6.线程池的状态

    • RUNNING:这是最正常的状态,接受新的任务,处理等待队列中的任务。
    • SHUTDOWN:不接受新的任务提交,但是会继续处理等待队列中的任务。
    • STOP:不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程。
    • TIDYING:所有的任务都销毁了,workCount 为 0,线程池的状态在转换为 TIDYING 状态时,会执行钩子方法 terminated()。
    • TERMINATED:terminated()方法结束后,线程池的状态就会变成这个

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u6zmXjQA-1608353586388)(D:\software\typora\workplace\imgs_thread\21.png)]

    五大状态:

    • running:能接受新任务以及处理已添加的任务;
    • shutdown:不接受新任务,可以处理已经添加的任务;
    • stop:不接受新任务,不处理已经添加的任务,并且中断正在处理的任务;
    • tiding:所有的任务已经终止,ctl记录的“任务数量”为0,ctl负责记录线程池的运行状态与活动线程数量
    • terminated:线程池彻底终止,则线程池转变为terminated状态。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HlLa8aAf-1608353586389)(D:\software\typora\workplace\imgs_thread\22.png)]

    7.线程池中submit()和exectute()方法的区别?

    • execute():只能执行Runnable类型的任务;而Runnable任务执行无返回值;
    • submit():可以执行runnable和callable类型的任务,其中runnable任务执行有返回值。

    8.线程池是如何保证线程安全的?

    ThreadPoolExecutor有一个ctl的原子整型变量。该ctl是一个AtomicInteger的类,就是让保存的int变量的更新都是原子操作,保证线程安全。

     private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    

    而线程池的生命状态和工作线程数是通过一个int整型变量来实现的。

    将int变量拆分为两部分使用,高3位记录线程池生命状态,而29位记录当前工作线程数。

    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
    
    // runState is stored in the high-order bits
    private static final int RUNNING    = -1 << COUNT_BITS;   // 高3位为111 running状态
    private static final int SHUTDOWN   =  0 << COUNT_BITS;   // 高3位为000 shutdown状态
    private static final int STOP       =  1 << COUNT_BITS;   // 高3位为001 stop状态
    private static final int TIDYING    =  2 << COUNT_BITS;   // 高3位为010 tiding状态
    private static final int TERMINATED =  3 << COUNT_BITS;   // 高3位为011 terminated状态 
    
    // Packing and unpacking ctl
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }
    
    展开全文
  • 使用线程池创建线程

    千次阅读 2019-03-10 05:22:26
    使用线程池创建线程的好处 阿里巴巴Java规约推荐使用线程池来创建线程而不是显式的创建线程,使用线程池来创建线程有如下好处 重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销. 线程是轻量级的...

    使用线程池创建线程的好处

    阿里巴巴Java规约推荐使用线程池来创建线程而不是显式的创建线程,使用线程池来创建线程有如下好处

    • 重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销.

      线程是轻量级的进程,虽然创建和销毁的开销比进程小得多,但仍是一笔不小的开销

    • 能有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象
    • 能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能

    Android中的线程池的概念来源于Java中的Executor,Executor是一个接口,真正的线程池的实现为ThreadPoolExecutor.

    ThreadPoolExecutor提供了一系列参数来配置线程池,通过不同的参数可以创建不同的线程池,从线程池的功能特性上来说,Android的线程池主要分为4类,这4类线程池可以通过Executors所提供的工厂方法来得到

    Android中的线程池都是直接或者间接通过配置ThreadPoolExecutor来实现的

    ThreadPoolExecutor

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

    各个参数代表的含义

    • corePoolSize

      线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使它们处于闲置状态.如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么闲置的核心线程在等待新任务到来时会有超时策略,这个时间间隔由keepAliveTime所指定,当等待时间超过keepAliveTime所指定的时长后,核心线程就会被终止.

    • maximumPoolSize

      线程池所能容纳的最大线程数,当活动线程数达到这个数值后,后续的新任务将会被阻塞

    • keepAliveTime

      非核心线程闲置时的超时时长,超过这个时长,非核心线程就会被回收,当ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true时,keepAliveTime同样会作用于核心线程

    • unit

      用于指定keepAliveTime参数的时间单位

    • workQueue

      阻塞队列,线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中

    • threadFactory

      线程工厂,为线程池提供创建新线程的功能,ThreadFactory是一个接口,它只有一个方法:Thread newThread(Runnable r)

    • RejectedExecutionHandler

      不常用,当线程池无法执行新任务时,这可能是由于任务队列已满或者是无法成功执行任务,这个时候ThreadPoolExecutor会调用handler的rejectedExecution方法来通知调用者,默认情况下rejectedExecution方法会直接抛出一个RejectedExecutionException,RejectedExecutionHandler提供了几个可选值:CallerRunsPolicy,AbortPolicy,DiscardPolicy和DiscardOldestPolicy,其中AbortPolicy是默认值,它会直接抛出RejectedExecutionException

    线程池的分类

    • FixedThreadPool

    通过Executors的newFixedThreadPool方法来创建.它是一种线程数量固定的线程池,当线程处于空闲状态时,它们并不会被回收,除非线程池被关闭了.当所有的线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来.由于FixedThreadPool只有核心线程并且这些核心线程不会被回收,这意味着它能够更加快速地响应外界的请求

    • CachedThreadPool

      通过Executors的newCachedThreadPool方法来创建.它是一种线程数量不定的线程池,它只有非核心线程,并且其最大线程数为Integer.MAX_VALUE.由于Integer.MAX_VALUE是一个很大的数,实际上就相当于最大线程数可以任意大.当线程池中的线程都处于活动状态时,线程池会创建新的线程来处理新任务,否则就会利用空闲的线程来处理新任务.线程池中的空闲线程都有超时机制,这个超时时长为60秒,超过60秒闲置线程就会被回收.

    • ScheduledThreadPool

      通过Executors的newScheduledThreadPool方法来创建.它的核心线程数量是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置时会被立即回收.ScheduledThreadPool这类线程池主要用于执行定时任务和具有固定周期的重复任务

    • SingleThreadExecutor

      通过Executors的newSingleThreadExecutor方法来创建.这类线程池内部只有一个核心线程,它确保所有的任务都在同一个线程中按顺序执行.SingleThreadExecutor的意义在于统一所有的外界任务到一个线程中,这使得在这些任务之间不需要处理线程同步的问题.

    但是…

    阿里巴巴不推荐使用Executors创建线程

    插图

    而是使用原始的ThreadPoolExecutor,这样会让人了解线程的规则.所以关于ThreadPoolExecutor的介绍就很重要了

    ??正文结束??
    展开全文
  • 线程池
  • 线程池创建问题

    2019-11-18 14:47:27
    从这四个函数来看,其实是分是否需要默认的线程池工厂handler。接下来就讲讲这些参数代表什么。 corePoolSize:核心线程数量。当线程数少于corePoolSize的时候,直接创建新的线程,尽管其他线程是空闲的。当线程池...
  • 那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务?在Java中可以通过线程池来达到这样的效果。下面我们来详细了解一下吧
  • Java线程池创建

    2020-06-23 08:50:37
    线程池的使用步骤: 1:使用线程池的工厂类Executors里面提供的静态方法newFixedThreadPool生产一...4:调用ExecutorService中的shutdown销毁线程池(不建议执行) public class ThreadPo { public static void main(S
  • 线程池的启动和销毁策略

    千次阅读 2019-03-16 12:17:33
    原文地址 版权声明:本文为博主原创文章,未经博主允许... 我们知道,受限于硬件、内存性能,我们不可能无限制的创建任意数量的线程,因为每一台机器允许的最大线程是一个有界值。也就是说ThreadPool...
  • 7、创建和销毁线程 获取管理线程池要用到的变量,task_num|ive_num|busy_num 根据既定算法,使用上述变量,判断是否应该创建销毁线程池中指定步长的线程 如果满足 创建条件 pthread_create(); 回调 任务...
  • 总结线程池创建的方式,详解线程池核心参数意义并分析ThreadPoolExecutor源码
  • 线程池通俗的讲就是在程序启动时创建好若干个线程,供程序去调度使用,当程序需要使用线程时不再需要去频繁的创建和销毁线程,而可以去线程池中获取空闲的线程直接使用,当使用完后线程进入空闲状态并非直接销毁...
  • 线程池创建和使用

    千次阅读 2018-09-19 17:12:34
    几种线程池创建和使用 目录: 1.newFixedThreadPool固定线程池 2.newSingleThreadExecutor一个线程的线程池 3.newCachedThreadPool缓存线程池 4.ThreadPoolExecutor 5.Future获取返回结果 1....
  • 概述 在Executor线程执行器框架中,提供了Executors这个工具类来创建指定的Executor实现类。在Executors工具类中提供了...线程池的核心线程数最大线程数固定且相同,任务等待队列无界: public st...
  • 线程池

    2019-05-07 16:04:00
    什么是线程池 创建销毁对象是非常耗费时间的 创建对象:需要分配内存等资源 销毁对象:虽然不需要程序员操心,但是垃圾回收器...可以避免频繁创建销毁、实现重复利用 JDK1.5 起,提供了内置线程池 线程池的好...
  • 大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。 那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务? 在Java中可以通过线程池来达到这样的效果。 新增...
  • 二、Executors中线程池创建的4种方式 newFixedThreadPool:创建的是定长的线程池,可以控制线程最大并发数,超出的线程会在线程队列中等待,使用的是无界队列,核心线程数最大线程数一样,当线程池中的线程没有...
  • 1)线程池: 一种线程使用模式,维护着多个线程,等待着监督管理者分配可并发执行的任务 线程池做的工作主要是控制运行的线程数量...通过重复利用已创建的线程,降低线程创建和销毁造成的消耗。 控制最大并发数:...
  • 提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中,可以避免频繁创建销毁、实现重复利用。 使用线程池好处: ①提高响应速度(减少了创建新线程的时间) ②降低资源消耗(重复利用线程池中线程,...
  • 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。2线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优监控。 提示:以下是本篇文章正文...
  • 创建线程池 执行说明 图解 步骤 1、创建线程池对线时候不会创建线程,当有第一个线程任务时,才会创建线程。 2、如果线程数少于核心线程数时,当有新的线程来,不管之前的线程是否空闲,都会继续创建新的线程,...
  • 通过使用线程池可以对线程实现复用,减少创建和销毁线程的次数,可以执行多个任务,大大降低系统资源消耗。 线程池的特点是,系统初始化会创建多个线程,放入线程池,需要使用的时候直接从线程池中取,不需要的时候...
  • // 设置使核心线程空闲时间超过keepAliveTime时间也被回收,这样线程池没有任务执行最终会自动销毁 executorService.allowCoreThreadTimeOut(true); Future<String> future = executorService.submit(callable...
  • 线程池创建

    2021-05-20 14:45:06
    1. 线程的创建会开辟本地方法栈、虚拟机栈、程序计数器作为线程私有的内存,同时消耗的时候需要销毁以上三个区域,因此频繁的创建和销毁比较消耗系统资源。 2. 在任务量远远大于线程可以处理的任务量的时候,并不能...
  • Java多线程-四种线程池创建方式

    千次阅读 2020-01-15 20:35:21
    1、减少线程的频繁创建销毁。 2、可有效的使用管理线程,提升资源重复使用率,避免资源不足引起的一些问题。 二)newFixedThreadPool 原理:创建一个固定数量大小的线程池,当线程任务数超过固定大小时,未...
  • 从输出可以看出,线程池中的线程执行了10次,由于创建了固定的线程池就只有3个,与预期的只有一个线程才能拿到线程变量有很大差距;造成这种现象是因为线程复用导致的; 测试代码: public class Demo { public ...
  • 线程池创建原理及实现

    千次阅读 2016-10-10 11:27:19
    本文给出了一个通用的线程池框架,该框架将与线程执行相关的任务进行了高层次的抽象,使之与具体的执行任务无关。另外该线程池具有动态伸缩性,它能根据执行任务的轻重自动调整线程池中线程的数量。文章的最后,我们...
  • 这里会介绍关于线程池的一些内容,比如线程池创建过程原理,如何理解线程池的各种状态以及之间的转换,理解面试中常说的FixedThreadPool,single Java语言关于线程池的类图: ThreadPoolExecutor就是我们口中...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 59,909
精华内容 23,963
关键字:

线程池创建和销毁