JavaEE多线程-线程池

线程池

线程池和和字符串常量池, 数据库连接池一样, 都是为了提高程序的运行效率, 减少开销。
随着并发程度的提高, 当我们去频繁的创建和销毁线程, 此时程序的开销还是挺大的, 为了进一步提高效率, 就引入了线程池, 程序中所创建的线程都会加载到一个 “池子” 中, 当程序需要使用线程的时候, 可以直接从池里面获取, 用完了就将线程还给池, 这样在多线程的环境中就不用去重复的创建和销毁线程, 从而使程序的运行效率提高, 线程池是管理线程的方式之一.

为什么从线程池中“拿”线程比直接创建线程更加高效?

1.第一点,线程池可以解决线程生命周期的系统开销问题,同时还可以加快响应速度。因为线程池中的线程是可以复用的,我们只用少量的线程去执行大量的任务,这就大大减小了线程生命周期的开销。而且线程通常不是等接到任务后再临时创建,而是已经创建好时刻准备执行任务,这样就消除了线程创建所带来的延迟,提升了响应速度,增强了用户体验。
2.第二点,线程池可以统筹内存和 CPU 的使用,避免资源使用不当。线程池会根据配置和任务数量灵活地控制线程数量,不够的时候就创建,太多的时候就回收,避免线程过多导致内存溢出,或线程太少导致 CPU 资源浪费,达到了一个完美的平衡。
3.第三点,线程池可以统一管理资源。比如线程池可以统一管理任务队列和线程,可以统一开始或结束任务,比单个线程逐一处理任务要更方便、更易于管理,同时也有利于数据统计,比如我们可以很方便地统计出已经执行过的任务的数量。

Java标准库提供的线程池

Java中提供了线程池相关的标准类ThreadPoolExecutor, 也被称作多线程执行器, 该类中的线程包括两类, 一类是核心线程, 另一类是非核心线程, 当核心线程都被占用还不能满足程序任务执行的需求时, 就会启用非核心线程, 直到任务量少了, 随之非核心线程也就会销毁.

jdk8中提供了4个构造方法, 这里主要介绍和理解参数最多的那一个构造方法, 其他构造方法只是基于这里的减少了参数而已.

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

  1. corePoolSize表示核心线程数
  2. maximumPoolSize表示最大线程数,就是核心线程数与非核心线程数之和
  3. keepAliveTime非核心线程最长等待新任务的时间, 超过此时间, 该线程就会被销毁
  4. unit上面参数的时间单位.
  5. workQueue线程池的任务队列(阻塞队列), 通过submit方法将任务注册到该队列中
  6. threadFactory线程工厂, 线程创建的方案.
  7. handler拒绝策略, 描述了当线程池任务队列满了, 如果继续添加任务会以什么样的方式处理.
  • 使用 Executors.newFixedThreadPool(10) 能创建出固定包含 10 个线程的线程池.
  • 返回值类型为 ExecutorService
  • 通过 ExecutorService.submit 可以注册一个任务到线程池中.
  • 在这里插入图片描述

    下面的是其他的几个构造方法:

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

    使用线程池时, 往往使用的是ExecutorServerce,ExecutorServerceThreadPoolExecutor所实现的一个接口, 其中最重要的一个方法是submit方法, 这个方法能够将任务交给线程池去执行.

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    //线程池
    //一般认为,纯用户态的操作,效率要比经过内核态处理的操作,要效果更高~~
    //线程池里面有什么?
    //1.先能够描述任务(直接用Runnable) 2.需要组织任务(直接用BlockingQueue) 3.能够描述工作线程  4.还需要组织这些线程 5.需要实现往线程池里添加任务
    public class Demo {
          
          
        public static void main(String[] args) {
          
          
            //创建一个固定线程数目的线程池,参数指定了线程个数
            ExecutorService pool = Executors.newFixedThreadPool(10);
            //创建一个自动扩容的线程池,会根据任务量来自动进行扩容
            //Executors.newCachedThreadPool();
            //创建一个只有一个线程的线程池
            //Executors.newSingleThreadExecutor();
            //创建一个带有定时器使用的线程池,类似于 Timer
            //Executors.newScheduledThreadPool();
    
            for (int i = 0; i < 100; i++) {
          
          
                pool.submit(new Runnable() {
          
          
                    @Override
                    public void run() {
          
          
                        System.out.println("Hello threadpool");
                    }
                });
            }
        }
    }
    
    

    Executors 创建线程池的几种方式

  • newFixedThreadPool: 创建固定线程数的线程池
  • newCachedThreadPool: 创建线程数目动态增长的线程池.
  • newSingleThreadExecutor: 创建只包含单个线程的线程池.
  • newScheduledThreadPool: 设定 延迟时间后执行命令,或者定期执行命令. 是进阶版的 Timer.
  • 线程池的实现

  • 任务, 可以直接使用`Runnable`实现.
  • 组织任务的数据结构, 使用阻塞队列`BlockingQueue`即可.
  • 若干个工作线程, 工作线程要通过一个循环不断的从阻塞队列中获取任务.
  • 注册任务的方法`submit,` 将任务添加到阻塞队列当中.
  • import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.LinkedBlockingQueue;
    
    class MyThreadPool {
          
          
        //使用阻塞队列来保存任务
        private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
    
        //这里创建出若干个工作线程,n表示线程的数量
        public MyThreadPool(int n) {
          
          
            for (int i = 0; i < n; i++) {
          
          
                Thread t = new Thread(() -> {
          
          
                    while (!Thread.interrupted()) {
          
          
                        try {
          
          
                            Runnable runnable = queue.take();
                            runnable.run();
                        } catch (InterruptedException e) {
          
          
                            throw new RuntimeException(e);
                        }
                    }
                });
                t.start();
            }
        }
    
        //注册任务给线程池
        public void submit(Runnable runnable) {
          
          
            try {
          
          
                queue.put(runnable);
            } catch (InterruptedException e) {
          
          
                throw new RuntimeException(e);
            }
        }
    }
    
    

猜你喜欢

转载自blog.csdn.net/weixin_61341342/article/details/129762234