zoukankan      html  css  js  c++  java
  • 什么是线程池?

    什么是线程池?

    线程池就是创建若干个可执行的线程放入一个池(容器)中,有任务需要处理时,会提交到线程池中的任务队列,处理完之后线程并不会被销毁,而是仍然在线程池中等待下一个任务。

    为什么要使用线程池?

    因为 Java 中创建一个线程,需要调用操作系统内核的 API,操作系统要为线程分配一系列的资源,成本很高,所以线程是一个重量级的对象,应该避免频繁创建和销毁。
    使用线程池就能很好地避免频繁创建和销毁。

    线程池是一种生产者——消费者模式

    先看下一个简单的 Java 线程池的代码

    package constxiong.concurrency.a010;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.BlockingQueue;
    
    /**
     * 简单的线程池
     * @author ConstXiong
     */
    public class ThreadPool {
        
        //阻塞队列实现生产者-消费者
        BlockingQueue<Runnable> taskQueue;
        
        //工作线程集合
        List<Thread> threads = new ArrayList<Thread>();
        
        //线程池的构造方法
        ThreadPool(int poolSize, BlockingQueue<Runnable> taskQueue) {
            this.taskQueue = taskQueue;
            
            //启动线程池对应 size 的工作线程
            for (int i = 0; i <poolSize; i++) {
                Thread t = new Thread(() -> {
                    while (true) {
                        Runnable task;
                        try {
                            task = taskQueue.take();//获取任务队列中的下一个任务
                            task.run();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
                t.start();
                threads.add(t);
            }
        }
        
        //提交执行任务
        void execute(Runnable task) {
            try {
                //把任务方法放到任务队列
                taskQueue.put(task);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
    }

    线程池的使用测试

    package constxiong.concurrency.a010;
    
    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.LinkedBlockingQueue;
    
    /**
     * 测试线程池的使用
     * @author ConstXiong
     */
    public class TestThreadPool {
    
        public static void main(String[] args) {
            // 创建有界阻塞任务队列
            BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>(10);
            // 创建 3个 工作线程的线程池
            ThreadPool tp = new ThreadPool(3, taskQueue);
            
            //提交 10 个任务
            for (int i = 1; i <= 10; i++) {
                final int j = i;
                tp.execute(() -> {
                    System.out.println("执行任务" + j);
                });
            }
        }
        
    }

    打印结果

    执行任务1
    执行任务2
    执行任务3
    执行任务6
    执行任务5
    执行任务4
    执行任务8
    执行任务7
    执行任务10
    执行任务9
    

    这个线程池的代码中

    • poolSize 是线程池工作线程的个数
    • BlockingQueue taskQueue 是用有界阻塞队列存储 Runnable 任务
    • execute(Runnable task) 提交任务
    • 线程池对象被创建,就自动启动 poolSize 个工作线程
    • 工作线程一直从任务队列 taskQueue 中取任务


    线程池的原理就是这么简单,但是 JDK 中的线程池的功能,要远比这个强大的多。

    JDK 中线程池的使用

    JDK 中提供的最核心的线程池工具类 ThreadPoolExecutor,在 JDK 1.8 中这个类最复杂的构造方法有 7 个参数。

    ThreadPoolExecutor(
        int corePoolSize,
        int maximumPoolSize,
        long keepAliveTime,
        TimeUnit unit,
        BlockingQueue<Runnable> workQueue,
        ThreadFactory threadFactory,
        RejectedExecutionHandler handler)
    corePoolSize:线程池保有的最小线程数。
    maximumPoolSize:线程池创建的最大线程数。
    keepAliveTime:上面提到项目根据忙闲来增减人员,那在编程世界里,如何定义忙和闲呢?很简单,一个线程如果在一段时间内,都没有执行任务,说明很闲,keepAliveTime 和 unit 就是用来定义这个“一段时间”的参数。也就是说,如果一个线程空闲了keepAliveTime & unit这么久,而且线程池的线程数大于 corePoolSize ,那么这个空闲的线程就要被回收了。
    unit:keepAliveTime 的时间单位
    workQueue:任务队列
    threadFactory:线程工厂对象,可以自定义如何创建线程,如给线程指定name。
    handler:自定义任务的拒绝策略。线程池中所有线程都在忙碌,且任务队列已满,线程池就会拒绝接收再提交的任务。handler 就是拒绝策略,包括 4 种(即RejectedExecutionHandler 接口的 4个实现类)。
    AbortPolicy:默认的拒绝策略,throws RejectedExecutionException
    CallerRunsPolicy:提交任务的线程自己去执行该任务
    DiscardPolicy:直接丢弃任务,不抛出任何异常
    DiscardOldestPolicy:丢弃最老的任务,加入新的任务


    JDK 的并发工具包里还有一个静态线程池工厂类 Executors,可以方便地创建线程池,但是由于 Executors 创建的线程池内部很多地方用到了无界任务队列,在高并发场景下,无界任务队列会接收过多的任务对象,导致 JVM 抛出OutOfMemoryError,整个 JVM 服务崩溃,影响严重。所以很多公司已经不建议使用 Executors 去创建线程。

    Executors 的简介

    虽然不建议使用,作为对 JDK 的学习,还是简单介绍一下.

    1. newFixedThreadPool:创建定长线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程数量不再变化,当线程发生错误结束时,线程池会补充一个新的线程
    2. newCachedThreadPool:创建可缓存的线程池,如果线程池的容量超过了任务数,自动回收空闲线程,任务增加时可以自动添加新线程,线程池的容量不限制
    3. newScheduledThreadPool:创建定长线程池,可执行周期性的任务
    4. newSingleThreadExecutor:创建单线程的线程池,线程异常结束,会创建一个新的线程,能确保任务按提交顺序执行
    5. newSingleThreadScheduledExecutor:创建单线程可执行周期性任务的线程池
    6. newWorkStealingPool:任务可窃取线程池,不保证执行顺序,当有空闲线程时会从其他任务队列窃取任务执行,适合任务耗时差异较大。


     


     

    所有资源资源汇总于公众号



     

  • 相关阅读:
    别忘了调用Page.IsValid
    在网站更新时使用App_Offline.htm
    转:零命令玩转Ubuntu 8.10(Wubi安装图文教程)
    转:画Web流程图的一点心得
    c#点对点聊天程序示例
    c#dns解析示例
    c++输出n以内素数问题(埃拉托色尼筛法)
    c#简单字符操作
    C++简单邮箱问题
    c#广播示例
  • 原文地址:https://www.cnblogs.com/ConstXiong/p/11955729.html
Copyright © 2011-2022 走看看