JavaEE——线程池

创建和销毁线程依赖于CPU,多线程操作时,如果频繁地创建和销毁线程,就会耗费大量的CPU资源。为了避免出现像这样的资源浪费以及提高系统的性能,就出现了线程池

线程池介绍

线程池类似于字符串常量池,如果需要用到某个字符串,JVM会先在字符串常量池中查找,如果有则直接调用,省去了创建的过程。线程池也一样,从线程池获取线程也省去了创建和销毁的过程。

使用线程池的优点

  • 重新利用而不是创建线程,降低了资源的损耗

  • 当获取新任务时,可以直接响应,不需要等待线程的创建,提高了响应速度

  • 线程池可以对线程进行统一管理、调控

线程池的参数介绍

线程池的类型是ThreadPollExecutor,其参数如下:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)
  • corePoolSize -> 核心线程数,线程池维护的最小线程数量,核心线程创建后不会被回收

  • maximumPoolSize -> 池中允许的最大线程数

  • keepAliveTime -> 当线程数大于内核时,这是多余的空闲线程在终止前等待新任务的最大时间

  • unit -> keepAliveTime参数的时间单位

  • workQueue -> 工作队列,新任务添加到队列中,执行任务时弹出

  • threadFactory -> 创建新线程时使用的工厂,可以设置线程名称和编号等

  • handler -> 拒绝策略,当线程池中的线程满了,工作队列已满,新添加任务时的拒绝方法。

标准库共提供了四种拒绝策略:

Modifier and Type

Class and Description

static class

ThreadPoolExecutor.AbortPolicy

抛出异常,系统停止运行

static class

ThreadPoolExecutor.CallerRunsPolicy

谁添加的任务谁执行

static class

ThreadPoolExecutor.DiscardOldestPolicy

丢弃最老的任务——即将要执行的任务

static class

ThreadPoolExecutor.DiscardPolicy

直接丢弃任务,不处理

标准库中的线程池

通过使用Executors类进行线程池的构造:

JavaEE——线程池_第1张图片

其中部分构造:

  • newFixedThreadPool: 创建固定线程数的线程池

  • newCachedThreadPool: 创建线程数目动态增长的线程池

  • newSingleThreadExecutor: 创建只包含单个线程的线程池

  • newScheduledThreadPool: 设定延迟时间后执行命令,或者定期执行命令。


通过submit方法添加任务到线程池的任务队列:

pool.submit(new Runnable() {
    @Override
    public void run() {
        //要执行的任务
    }
});

线程池的工作流程

  1. 当线程数小于corePoolSize时,每提交一个任务,创建一个新线程;

  1. 线程数达到corePoolSize,提交的新任务会放到workQueue中进行阻塞等待。

  1. workQueue也满以后,继续创建新线程来处理,直到线程数达到maximumPoolSize。

  1. 当前线程数达到maximumPoolSize后并且workQueue也满,则根据handler处理。

  1. 线程数大于corePoolSize时,空闲线程最多等待keepAliveTime unit,若一直处于空闲状态则回收。

线程池的使用案例

public static void main(String[] args) {
    //获取有10个线程的线程池
    ExecutorService pool = Executors.newFixedThreadPool(10);
    //执行100个任务
    for (int i = 0; i < 100; i++) {
        int num = i;
        pool.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello" + num);
            }
        });
    }
    System.out.println("任务结束!");
}
JavaEE——线程池_第2张图片

我们发现任务并不是按照顺序执行的,同时任务虽然完成了,线程却没有结束。

这是因为线程是并发执行的,同时工作队列为阻塞队列,因此不会自主结束。

模拟实现线程池

//水平有限,这里只实现前两个过程
public class MyThreadPool {
    private int maxWorkerCount = 10;//最大线程数
    private int size = 0;//当前线程数
    private LinkedBlockingQueue queue = new LinkedBlockingQueue<>();

    public void submit(Runnable runnable) throws InterruptedException {
        //当线程小于核心线程数时,每添加一个新任务创建一个线程
        if (size < maxWorkerCount) {
            Thread t = new Thread(runnable);
            t.start();
            size++;
        } else {
            //线程数大于核心线程数时,不再创建新线程,而是放到阻塞队列中等待
            queue.put(runnable);
            Thread t1 = new Thread(() -> {
                try {
                    while (!Thread.interrupted()) {
                        Runnable runnable1 = queue.take();
                        runnable1.run();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            t1.start();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyThreadPool myThreadPool = new MyThreadPool();
        for (int i = 0; i < 100; i++) {
            int num = i;
            myThreadPool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello" + num);
                }
            });
        }
    }
}

运行得到和之前差不多的结果:

JavaEE——线程池_第3张图片

你可能感兴趣的:(java,jvm)