ThreadPoolExecutor 线程池

​​​​​​​

目录

前言

1.Executor 接口介绍

2.使用 Executors 工厂类创建线程池 

1.使用 newCachedThreadPool() 方法创建无界线程池 

2.验证 newCachedThreadPool() 方法创建线程池和线程复用特性 

3.使用 newCachedThreadPool(ThreadFactory) 方法定制线程工厂

4.使用 newCachedThreadPool() 方法创建无界线程池的缺点 

5.使用 newFixedThreadPool(int) 方法创建有界线程池 

6.使用 newSingleThreadExecutor() 方法创建单一线程 

3.ThreadPoolExecutor

构造方法参数详解

总结


​​​​​​​

前言

在开发服务器端软件项目时,软件经常需要处理执行时间很短而数目巨大的请求,如果为每一个请求创建一个新的线程,则会导致性能上的瓶颈。因为JVM 需要频繁地处理线程对象的创建和销毁,如果请求的执行时间很短,则有可能花在创建和销毁线程对象上的时间大于真正执行任务的时间,所以系统性能会大幅降低。
JDK 5及以上版本提供了对线程池的支持,主要用于支持高并发的访问处理,并且复用线程对象。线程池核心原理是创建一个“线程池”(ThreadPool),在池中对线程对象进行管理包括创建与销毁,使用池时只需要执行具体的任务即可,线程对象的处理都在池中被封装了。线程池类ThreadPoolExecutor实现了 Executor 接口,该接口是学习线程池的重点,因为掌握了该接口中的方法也就大致掌握了 ThreadPolExecutor类的主要功能了


1.Executor 接口介绍

在介绍线程池之前,要先了解一下接口 java.util.concurrent.Executor,与线程池有关的大部分类都要实现此接口,该接口的声明如图所示。 

ThreadPoolExecutor 线程池_第1张图片

此接口的结构非常简洁,仅有一个方法,如图:

但Executor 是接口,并不能直接使用,所以还需要实现类,图中所示的内容就是完整的Executor接口相关的类继承结构。 此图相当重要,它概括了Executor 祖先接口下的全部实现类与继承关系。

ThreadPoolExecutor 线程池_第2张图片

ExecutorService 接口是 Executor 的子接口,在内部添加了比较多的方法,其内部结构如图所示。

ThreadPoolExecutor 线程池_第3张图片虽然 ExecutorService 接口添加了若干个方法的定义,但是还是不能实例化,那么就要看它的唯一子实现类 AbstractExecutorService 类的名称来看,它是 abstract (抽象)的,查看源代码也的确是抽象类,具体如下:

public abstract class AbstractExecutorService implements ExecutorService {

ThreadPoolExecutor 线程池_第4张图片

所以 AbstractExecutorService 类同样是不能实例化的。 

再来看一下 AbstractExecutorService 类的子类 ThreadPoolExecutor 的源代码,具体如下:

public class ThreadPoolExecutor extends AbstractExecutorService {

通过查看代码发现,ThreadPoolExecutor 类并不是抽象的,所以可以进行实例化,进而可以使用 ThreadPoolExecutor 类的方法所提供的功能。

ThreadPoolExecutor 类的方法列表如图:

ThreadPoolExecutor 线程池_第5张图片

2.使用 Executors 工厂类创建线程池 

Executor 接口仅仅是一种规范、一种声明、一种定义,并没有实现任何的功能,所以大多数的情况下,需要使用接口的实现类来完成指定的功能,比如 ThreadPoolExecutor 类就是 Executor 的实现类,但 ThreadPoolExecutor 类在使用上并不是那么方便在实例化时需要传人多个参数,还要考虑线程的并发数等与线程池运行效率有关的参数,所以官方建议使用 Executors 工厂类来创建线程池对象,该类对创建 ThreadPoolExecutor 线程池进行封装,直接调用即可。

ThreadPoolExecutor 线程池_第6张图片

1.使用 newCachedThreadPool() 方法创建无界线程池 

使用 Executors 类的 newCachedThreadPool() 方法创建无界线程池,可以进行线程自动回收。所谓 "无界线程池" 就是池中存放线程个数是理论上的最大值,即 Interger.MAX_VALUE。 

创建测试用例,代码如下:

package org.example.Executor;

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

public class Executor {
    public static void main(String[] args) {
        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("Runnable1 begin "+System.currentTimeMillis());
                    Thread.sleep(1000);
                    System.out.println("A");
                    System.out.println("end");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("Runnable2 begin "+System.currentTimeMillis());
                    Thread.sleep(1000);
                    System.out.println("B");
                    System.out.println("end");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

    }
}

运行结果如图: 

ThreadPoolExecutor 线程池_第7张图片

从打印的时间来看,A 和 B 几乎是在相同的时间开始打印,也就是创建了 2 个线程,而且 2 个线程之间是异步运行的。

2.验证 newCachedThreadPool() 方法创建线程池和线程复用特性 

之前的实验没有验证 newCachedThreadPool() 方法创建的是线程池,在下面的测试中进行验证。

package org.example.Executor;

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

public class Executors_2 {
    static class MyRunnable implements Runnable {
        private String username;

        public MyRunnable(String username) {
            this.username = username;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + "username=" + username + " begin" + System.currentTimeMillis());
            System.out.println(Thread.currentThread().getName() + " username=" + username + "     end" + System.currentTimeMillis());
           /* try {
                //Thread.sleep(2000);

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

    public static void main(String[] args) throws InterruptedException {
        ExecutorService service = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++) {
            service.execute(new MyRunnable(" " + (i + 1)));
        }
        Thread.sleep(1000);
        System.out.println();
        System.out.println();
        for (int i = 0; i < 5; i++) {
            service.execute(new MyRunnable(" " + (i + 1)));
        }
    }
}

运行结果如图:

ThreadPoolExecutor 线程池_第8张图片

线程复用对象了,并且可知,线程池中线程对象只有处于闲置状态时,才可以被复用。 

3.使用 newCachedThreadPool(ThreadFactory) 方法定制线程工厂

无界线程池中创建线程类的过程是可以定制的,我们可以使用 newCachedThreadPool(ThreadFactory)方法来解决这个问题。

创建测试用例,线程工厂代码如下:

    static class MyThreadFactory implements ThreadFactory{

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r);
            thread.setName("自定义线程池中线程对象的名称:"+Math.random());
            return thread;
        }
    }

运行 main 方法:

    public static void main(String[] args) {
        MyThreadFactory myThreadFactory = new MyThreadFactory();
        ExecutorService executorService = Executors.newCachedThreadPool(myThreadFactory);
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("我在运行" + System.currentTimeMillis() + " " + Thread.currentThread().getName());
            }
        });
    }

运行结果如图:

通过使用自定义的 ThreadFactory 接口实现类,实现了线程对象的定制性。 

ThreadPoolExecutor、ThreadFactory 和 Thread 之间的关系是 ThreadPoolExecutor 类使用 ThreadFactory 方法来创建 Thread 对象。

内部源代码如下:

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

在源代码中使用了默认线程工厂,源代码如下:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }

Executors.defaultThreadFactory() 方法源代码如下:

    public static ThreadFactory defaultThreadFactory() {
        return new DefaultThreadFactory();
    }

DefaultThreadFactory() 类实现关系如下:

 static class DefaultThreadFactory implements ThreadFactory

从以上源代码分析中可得知,使用无参数的 public static ExecutorService newCachedThreadPool() 方法创建线程池时,在内部隐式的使用了 DefaultThreadFactory 类。

4.使用 newCachedThreadPool() 方法创建无界线程池的缺点 

如果在高并发的情况下 ,使用 newCachedThreadPool() 方法创建无界线程池极易造成内存占用率大幅升高,导致内存溢出或者系统运行效率严重下降。

创建测试用例:

package org.example.Executor;

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

public class newCachedThreadPool_No {
    public static void main(String[] args) {
        ExecutorService es = Executors.newCachedThreadPool();
        for (int i = 0; i < 200000; i++) {
            es.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("runnable begin " + Thread.currentThread()
                                .getName() + " "
                                + System.currentTimeMillis());
                        Thread.sleep(1000 * 60 * 5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

程序运行后在 ”任务管理器“ 中查看 ”可用内存“ 极速下降,系统运行效率大幅度降低,超大的内存空间都被 Thread 类对象占用了,无界线程池对线程的数量并没有控制,这时可以尝试使用有界线程池来限制线程池占用的最大空间。 

5.使用 newFixedThreadPool(int) 方法创建有界线程池 

newFixedThreadPool(int) 方法创建的是有界线程池,也就是池中的线程个数可以指定最大数量。 

新建测试用例:

package org.example.Executor;

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

public class Exectors_3 {
    static class MyRunnable implements Runnable {
        private String username;

        public MyRunnable(String username) {
            this.username = username;
        }

        @Override
        public void run() {
            try {
                System.out.println(Thread.currentThread().getName() + " username=" + username + " begin " + System.currentTimeMillis());
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + " username=" + username + "     end" + System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 6; i++) {
            executorService.execute(new MyRunnable("" + (i + 1)));
        }
    }
}

运行结果如图:

ThreadPoolExecutor 线程池_第9张图片

由图可知使用有界贤臣池之后线程池中的最多线程个数是可控的。 

6.使用 newSingleThreadExecutor() 方法创建单一线程 

使用 newSingleThreadExecutor() 方法可以创建单一线程池,单一线程池可以实现队列的方式来执行任务。

新建测试用例:

package org.example.Executor;

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

public class Exectors_4 {
    static class MyRunnable implements Runnable {
        private String username;

        public MyRunnable(String username) {
            this.username = username;
        }

        @Override
        public void run() {
            try {
                System.out.println(Thread.currentThread().getName() + " username=" + username + " begin " + System.currentTimeMillis());
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + " username=" + username + "     end" + System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 3; i++) {
            executorService.execute(new MyRunnable(" " + (i + 1)));
        }
    }
}

程序运行效果如图:

ThreadPoolExecutor 线程池_第10张图片

3.ThreadPoolExecutor

ThreadPoolExecutor 类可以非常方便地创建线程池对象,而不需要程序员设计大量的 new 实例化 Thread 相关的代码。

前面使用 Executors 类中的 newXXXThreadExecutor() 方法可以快速地创建线程池,但创建的细节未知,因为已经被封装处理,查看 newSingleThreadExecutor() 方法时,其内部其实实例化了一个 ThreadPoolExecutor 类的实例,源代码如下:

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

构造方法参数详解

ThreadPoolExecutor 类参数最全的构造方法如下:

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

参数解释如下: 

  • corePoolSize:池中至少要保留的线程数,该属性就是定义 corePool 核心池的大小。
  • maximumPoolSize:池中允许的最大线程数,maximumPoolSize 包含 corePoolSize。
  • keepAliveTime:当线程数量大于 corePoolSize 值时,在没有超过指定的时间内是不能从线程池中将空闲的线程删除的,如果超过此时间单位,则删除空闲线程。为 0 时,执行完任务立即删除此线程。”能删除的空闲线程“范围是 maximumPoolSize ~ corePoolSize,也就是 corePoolSize 之外的线程。
  • unit:keepAliveTime 参数的时间单位。
  • workQueue:执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的任务。使用 ThreadPoolExecutor 类时一般会传入 LinkedBlockingQueue SynchronousQueue 这两个队列。
  • threadFactory:创建线程的工厂,可以是自定义的。
  • handler:是一个接口类型,用来指定当任务被拒绝执行时的处理策略。当线程池已经达到了最大线程数,且工作队列也已经满了的时候,如果还有新的任务提交到线程池,这些任务会被拒绝。在这种情况下,RejectedExecutionHandler 对象就会被调用。

注意,所谓的空闲线程就是没有执行任务的线程,不管这个线程在哪里,只要不执行任务,它就是空闲的。 

为了更好的理解这些参数在使用上的一些关系,下面对它们进行详细的讲解。

  • A 代表 execute(runnable) 要执行的 task 任务数量,如图所示。 

  • B 代表 corePoolSize,见图。
  • C 代表 maximumPoolSize ,见图。

ThreadPoolExecutor 线程池_第11张图片

  • D 代表 A - B(假设 A > B)的值。

构造方法中前 5 个参数之间关系较为紧密,但从使用的效果来讲,不同类型的队列能影响 ThreadPool 线程池执行的行为,所以后面的分析过程就以 LinkedBlockingQueue 和 SynchronousQueue 为主线,总结如下:

1.使用无参 new LinkedBlockingQueue() 队列的情况。

  • 注意,使用无参new LinkedBlockingQueue()队列的特点就是只使用核心池中的线程执行任务。
  1. 如果 A≤B,立即在 corePool核心池中创建线并运行任务,这些任务并不会放人LinkedBlockingQueue 中,构造方法参数maximumPoolSize、keepAliveTime和unit将被忽略。
  2. 如果 A>B && A≤C,构造方法参数maximumPoolSize、keepAliveTime和unit将被忽略,并把D放人 LinkedBlockingQueue 中等待被核心池中的线程执行。
  3. 如果 A>C,构造方法参数 maximumPoolSize、keepAliveTime 和 unit 将被忽略并把D放人  LinkedBlockingQueue 中等待被核心池中的线程执行。

2.使用 SynchronousQueue 队列的情况。

  1. 如果 A≤B,立即在 corePool 核心池中创建线程并运行任务,这些任务并不放人SynchronousQueue 中,构造方法参数maximumPoolSize、keepAliveTime和unit将被忽略。
  2. 如果 A>B&& A≤C,则构造方法参数 maximumPoolSize、keepAliveTime和unit有效,并且马上创建最多C个线程运行这些任务,而不把D放入 SynchronousQueue 队列中,D执行完任务后在指定 keepAliveTime 时间发生超时时,将D进行清除,如果D在keepAliveTime时间之后未完成任务,则在D完成任务后进行清除。
  3. 如果 A>C,则最多处理 C个任务,其他任务(不包括核心池中的任务)不再处理并抛出异常。

3.使用new LinkedBlockingQueue(xxxxx) 队列有参的情况。其中,参数xxxxx代表队列的最大存储长度。

  • 注意,使用有参 new LinkedBlockingQucue(xxxxx) 队列的执行特点是心池中的线程和maximumPoolSize-corePoolSize 线有可能一起执行任务,也就是多执行任务的线程数量就是 maximumPoolSize。另外在使用有参new LinkedBlockingQueue(xxxx)队列时,执行的流程是先判断 corePoolSize 大小够不够,如果不够则向 new LinkedBlockingOueue(xxxx)刚中存储,如果 new LinkedBlockingQueue(xxxxx)队列中放不下,则将剩余的任务尝试向 C-B 中存放,如果 C-B 放不下就报异常。
  1. 如果 A≤B,立即在 corePool 核心池中创建线程并运行任务,这些任务并不放入new LinkedBlockingQueue(xxxxx)队列中,构造方法参数 maximumPoolSize、keepAliveTime和unit 将被忽略。
  2. 如果 A>B 且 (A-B) ≤ xxxxx,立即在 corePool 核心池中创建线程并运行任务,构浩方法参数 maximumPoolSize keepAliveTime 和 unit 被忽略,并把 (A-B) 放人LinkedBlockingOueue(xxxxx)队列中等待被核心池中的线程执行。
  3. 如果A>B、(A-B)>xxxxx,并且 (A-B-xxxxx)<(C-B),立即在corePool核心池中创建线程并运行任务,构造方法参数 maximumPoolSize、keepAliveTime和unit有效,并且马上创建 (A-B-xxxxx) 个线程运行这些任务,(A-B-xxxxx)执行完任务后在指定 keepAliveTime 时间发生超时时,将(A-B-xxxxx)进行清除,如果 (A-B-xxxxx) 在keepAliveTime时间之后未完成任务,则在(A-B-xxxxx)完成任务后进行清除。
  4. 如果A>B、(A-B)>xxxxx,并且(A-B-xxxxx)>(C-B),立即在corePool核心池中创建线程并运行任务,构造方法参数 maximumPoolSize、keepAliveTime和unit有效,马上创建(C-B)个线程运行这些任务,(C-B)个任务执行完任务后在指定 keepAliveTime时间发生超时时,将(C-B)进行清除,如果(C-B)在 keepAliveTime 时间之后未完成任务,则在(C-B)完成任务后进行清除,(A-B-xxxxx)-(C-B)多出来的任务被拒绝执行并出现异常。 

1.构造方法前 2 个参数与 getCorePoolSize() 和 getMaximumPoolSize() 方法 

新建测试用例:

package org.example.Executor;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolExecutor_1 {
    //获取基本属性 corePoolSize 和 maximumPoolSize
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(7,8,5, TimeUnit.SECONDS,new LinkedBlockingQueue<>());
        System.out.println(executor.getCorePoolSize());
        System.out.println(executor.getMaximumPoolSize());
        System.out.println("");
        executor = new ThreadPoolExecutor(7,8,5, TimeUnit.SECONDS,new SynchronousQueue<>());
        System.out.println(executor.getCorePoolSize());
        System.out.println(executor.getMaximumPoolSize());

    }
}

从代码中可以分析出,线程池中保存的 core 线程数是7,最大为8,运行结果如图。 

ThreadPoolExecutor 线程池_第12张图片

2.验证 (1.1)和(2.1)的情况

(1.1)测试用例:

package org.example.Executor;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Run2_1 {
    // 线程数量小于等于 corePoolSize
    // keepAliveTime 大于 5 时也不清除空闲线程
    // 因为空闲线程在 corePool 中
    // corePool 中的线程是不能被删除的
    // 所以 keepAliveTime 参数无效
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try{
                    System.out.println(Thread.currentThread().getName()+" run!"+System.currentTimeMillis());
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        ThreadPoolExecutor executor = new ThreadPoolExecutor(7,8,5, TimeUnit.SECONDS,new LinkedBlockingQueue<>());
        executor.execute(runnable);//1
        executor.execute(runnable);//2
        executor.execute(runnable);//3
        executor.execute(runnable);//4
        executor.execute(runnable);//5
        executor.execute(runnable);//6
        executor.execute(runnable);//7
        Thread.sleep(300);
        //可以看成 车中可载人的标准人数
        System.out.println("A executor.getCorePoolSize()="+executor.getCorePoolSize());
        //车中可载人的最大人数
        System.out.println("A executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
        //车中正在载的人数
        System.out.println("A executor.getPoolSize()="+executor.getPoolSize());
        //站在地面上等待被送的人数
        System.out.println("A executor.getQueue().size()="+executor.getQueue().size());
        Thread.sleep(10000);
        System.out.println(" 10s 后打印结果。");
        System.out.println("B executor.getCorePoolSize()="+executor.getCorePoolSize());
        System.out.println("B executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
        System.out.println("B executor.getPoolSize()="+executor.getPoolSize());
        System.out.println("B executor.getQueue().size()="+executor.getQueue().size());
    }
    //按钮呈红色,因为池中还有线程在等待任务。
}

运行结果如图,由于线程数量小于等于 7,打印信息 ”B executor.getCorePoolSize()=7“说明核心线程超过 5 s,不清除。7个线程对象成功运行,说明线程池成功工作了,符合(1.1)情况:

ThreadPoolExecutor 线程池_第13张图片

测试(2.1)只需要更改使用队列为 SynchronousQueue 结果与上方并无差别,说明只要线程数量小于等于 corePoolSize 就不清除空闲线程,而且和使用队列无关。

3.验证(1.2)的情况 

package org.example.Executor;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Run2_1 {
    // 队列使用 LinkedBlockingQueue,也就是如果
    // 线程数量大于 corePoolSize 并且小于等于 maximumPoolSize时将maximumPoolSize-corePoolSize的任务放入队列中
    // 同一时间最多只能有 7 个线程在运行
    // 如果使用 LinkedBlockingQueue 类则 maximumPoolSize 参数作用将忽略
    // 因为 任务都放入 LinkedBlockingQueue 队列中了
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try{
                    System.out.println(Thread.currentThread().getName()+" run!"+System.currentTimeMillis());
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        ThreadPoolExecutor executor = new ThreadPoolExecutor(7,8,5, TimeUnit.SECONDS,new LinkedBlockingQueue<>());
        executor.execute(runnable);//1
        executor.execute(runnable);//2
        executor.execute(runnable);//3
        executor.execute(runnable);//4
        executor.execute(runnable);//5
        executor.execute(runnable);//6
        executor.execute(runnable);//7
        executor.execute(runnable);//8
        Thread.sleep(300);
        //可以看成 车中可载人的标准人数
        System.out.println("A executor.getCorePoolSize()="+executor.getCorePoolSize());
        //车中可载人的最大人数
        System.out.println("A executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
        //车中正在载的人数
        System.out.println("A executor.getPoolSize()="+executor.getPoolSize());
        //站在地面上等待被送的人数
        System.out.println("A executor.getQueue().size()="+executor.getQueue().size());
        Thread.sleep(10000);
        System.out.println(" 10s 后打印结果。");
        System.out.println("B executor.getCorePoolSize()="+executor.getCorePoolSize());
        System.out.println("B executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
        System.out.println("B executor.getPoolSize()="+executor.getPoolSize());
        System.out.println("B executor.getQueue().size()="+executor.getQueue().size());
    }
    //按钮呈红色,因为池中还有线程在等待任务。
    // LinkedBlockingQueue abc;
}

8 个线程成功运行,从结果来看,完全符合(1.2)的情况。BlockingQueue 只是一个接口,常用的实现类有 LinkedBlockingQueue 和 ArrayBlockingQueue。用 LinkedBlockingQueue 的好处在于可以没有大小限制,有点是队列容量非常大,而线程池中运行的线程数也永远不会超过 corePoolSize 值,因为其他多余的线程被放入 LinkedBlockingQueue 中,keepAliveTime 参数也就没有意义了。

ThreadPoolExecutor 线程池_第14张图片

4.验证 (2.2) 的情况

package org.example.Executor;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Run2_1 {
    // 队列使用 SynchronousQueue,也就是如果
    // 线程数量大于 corePoolSize 时将任务放入池中,总数量为 8
    // 没有超过 maximumPoolSize 值
    // 由于运行的线程数为 8 ,因此数量上大于corePoolSize为7的值
    //所以 keepAliveTime > 5 时清除空闲线程
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try{
                    long ok = System.currentTimeMillis();
                    System.out.println(Thread.currentThread().getName()+" run!"+System.currentTimeMillis());
                    Thread.sleep(15000);
                    System.out.println("    endTime="+(System.currentTimeMillis()-ok));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        ThreadPoolExecutor executor = new ThreadPoolExecutor(7,8,7, TimeUnit.SECONDS,new SynchronousQueue<>());
        executor.execute(runnable);//1
        executor.execute(runnable);//2
        executor.execute(runnable);//3
        executor.execute(runnable);//4
        executor.execute(runnable);//5
        executor.execute(runnable);//6
        executor.execute(runnable);//7
        executor.execute(runnable);//8
        Thread.sleep(1000);
        //可以看成 车中可载人的标准人数
        System.out.println("A executor.getCorePoolSize()="+executor.getCorePoolSize());
        //车中可载人的最大人数
        System.out.println("A executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
        //车中正在载的人数
        System.out.println("A executor.getPoolSize()="+executor.getPoolSize());
        //站在地面上等待被送的人数
        System.out.println("A executor.getQueue().size()="+executor.getQueue().size());
        Thread.sleep(10000);
        System.out.println(" 10s 后打印结果。");
        System.out.println("B executor.getCorePoolSize()="+executor.getCorePoolSize());
        System.out.println("B executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
        System.out.println("B executor.getPoolSize()="+executor.getPoolSize());
        System.out.println("B executor.getQueue().size()="+executor.getQueue().size());
        Thread.sleep(10000);
        System.out.println(" 20s 后打印结果。");
        System.out.println("C executor.getCorePoolSize()="+executor.getCorePoolSize());
        System.out.println("C executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
        System.out.println("C executor.getPoolSize()="+executor.getPoolSize());
        System.out.println("C executor.getQueue().size()="+executor.getQueue().size());
    }
    //按钮呈红色,因为池中还有线程在等待任务。
    // LinkedBlockingQueue abc;
}

符合(2.2)的情况,同时”如果D在keepAliveTime时间之后未完成任务,则在D完成任务后进行清除“测试成功,执行任务后空闲时间一过立即从线程池中被清除。

ThreadPoolExecutor 线程池_第15张图片

5.验证(1.3)的情况 

package org.example.Executor;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Run4_1 {
    //队列使用 LinkedBlockingQueue类
    //并且线程数量大于 corePoolSize 时将其余的任务放入队列中
    //同一时间最多只能有 7 个线程在运行
    //所以 keepAliveTime 大于 5 时也不清除空闲线程
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try{
                    System.out.println(Thread.currentThread().getName()+" run!"+System.currentTimeMillis());
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        ThreadPoolExecutor executor = new ThreadPoolExecutor(7,8,5, TimeUnit.SECONDS,new LinkedBlockingQueue<>());
        executor.execute(runnable);//1
        executor.execute(runnable);//2
        executor.execute(runnable);//3
        executor.execute(runnable);//4
        executor.execute(runnable);//5
        executor.execute(runnable);//6
        executor.execute(runnable);//7
        executor.execute(runnable);//8
        executor.execute(runnable);//9
        Thread.sleep(300);
        //可以看成 车中可载人的标准人数
        System.out.println("A executor.getCorePoolSize()="+executor.getCorePoolSize());
        //车中可载人的最大人数
        System.out.println("A executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
        //车中正在载的人数
        System.out.println("A executor.getPoolSize()="+executor.getPoolSize());
        //站在地面上等待被送的人数
        System.out.println("A executor.getQueue().size()="+executor.getQueue().size());
        Thread.sleep(10000);
        System.out.println(" 10s 后打印结果。");
        System.out.println("B executor.getCorePoolSize()="+executor.getCorePoolSize());
        System.out.println("B executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
        System.out.println("B executor.getPoolSize()="+executor.getPoolSize());
        System.out.println("B executor.getQueue().size()="+executor.getQueue().size());
    }
    //通过此实验可以得知,如果使用 LinkedBlockingQueue 作为任务队列。
    //则不管线程数大于 corePoolSize 还是大于 maximumPoolSize
    //都将多余的任务放入队列中
    //程序都可以正确无误的运行,并且不会出现异常
    //此时按钮呈红色,因为池中还有线程在等待任务。
}

程序运行结果如下,符合(1.3)的情况:

ThreadPoolExecutor 线程池_第16张图片

6.验证(2.3) 的情况

package org.example.Executor;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Run4_1 {
    //队列使用 SynchronousQueue
    //并且线程数量大于 corePoolSize
    //并且线程数量大于 maximumPoolSize
    //所以出现异常
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try{
                    System.out.println(Thread.currentThread().getName()+" run!"+System.currentTimeMillis());
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        ThreadPoolExecutor executor = new ThreadPoolExecutor(7,8,5, TimeUnit.SECONDS,new SynchronousQueue<>());
        executor.execute(runnable);//1
        executor.execute(runnable);//2
        executor.execute(runnable);//3
        executor.execute(runnable);//4
        executor.execute(runnable);//5
        executor.execute(runnable);//6
        executor.execute(runnable);//7
        executor.execute(runnable);//8
        executor.execute(runnable);//9
        Thread.sleep(300);
        //可以看成 车中可载人的标准人数
        System.out.println("A executor.getCorePoolSize()="+executor.getCorePoolSize());
        //车中可载人的最大人数
        System.out.println("A executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
        //车中正在载的人数
        System.out.println("A executor.getPoolSize()="+executor.getPoolSize());
        //站在地面上等待被送的人数
        System.out.println("A executor.getQueue().size()="+executor.getQueue().size());
        Thread.sleep(10000);
        System.out.println(" 10s 后打印结果。");
        System.out.println("B executor.getCorePoolSize()="+executor.getCorePoolSize());
        System.out.println("B executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
        System.out.println("B executor.getPoolSize()="+executor.getPoolSize());
        System.out.println("B executor.getQueue().size()="+executor.getQueue().size());
    }

}

运行了 8 个任务,其他的任务都没有运行,从结果来看,符合(2.3)情况。

ThreadPoolExecutor 线程池_第17张图片

7.验证 (3.2)的情况

package org.example.Executor;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class RunX_1 {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try{
                    System.out.println(Thread.currentThread().getName()+" run!"+System.currentTimeMillis());
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        ThreadPoolExecutor executor = new ThreadPoolExecutor(3,6,5, TimeUnit.SECONDS,new LinkedBlockingQueue<>(2));
        executor.execute(runnable);//1
        executor.execute(runnable);//2
        executor.execute(runnable);//3
        executor.execute(runnable);//4
        Thread.sleep(300);
        //可以看成 车中可载人的标准人数
        System.out.println("A executor.getCorePoolSize()="+executor.getCorePoolSize());
        //车中可载人的最大人数
        System.out.println("A executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
        //车中正在载的人数
        System.out.println("A executor.getPoolSize()="+executor.getPoolSize());
        //站在地面上等待被送的人数
        System.out.println("A executor.getQueue().size()="+executor.getQueue().size());
        Thread.sleep(800);
        System.out.println(" 800 后打印结果。");
        System.out.println("B executor.getCorePoolSize()="+executor.getCorePoolSize());
        System.out.println("B executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
        System.out.println("B executor.getPoolSize()="+executor.getPoolSize());
        System.out.println("B executor.getQueue().size()="+executor.getQueue().size());
        Thread.sleep(1000);
        System.out.println(" 1000 后打印结果。");
        System.out.println("C executor.getCorePoolSize()="+executor.getCorePoolSize());
        System.out.println("C executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
        System.out.println("C executor.getPoolSize()="+executor.getPoolSize());
        System.out.println("C executor.getQueue().size()="+executor.getQueue().size());
    }

}

运行结果如图,完全符合(3.2)的情况:

ThreadPoolExecutor 线程池_第18张图片

8.验证(3.3)的情况

package org.example.Executor;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class RunX_1 {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try{
                    System.out.println(Thread.currentThread().getName()+" run!"+System.currentTimeMillis());
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        ThreadPoolExecutor executor = new ThreadPoolExecutor(3,6,5, TimeUnit.SECONDS,new LinkedBlockingQueue<>(2));
        //使用 core 队列中的线程
        executor.execute(runnable);//1
        executor.execute(runnable);//2
        executor.execute(runnable);//3
        //使用第三方队列中的线程
        executor.execute(runnable);//1
        executor.execute(runnable);//2
        // C-B
        executor.execute(runnable);//1
        executor.execute(runnable);//2
        executor.execute(runnable);//3

        Thread.sleep(300);
        //可以看成 车中可载人的标准人数
        System.out.println("A executor.getCorePoolSize()="+executor.getCorePoolSize());
        //车中可载人的最大人数
        System.out.println("A executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
        //车中正在载的人数
        System.out.println("A executor.getPoolSize()="+executor.getPoolSize());
        //站在地面上等待被送的人数
        System.out.println("A executor.getQueue().size()="+executor.getQueue().size());
        Thread.sleep(800);
        System.out.println(" 800 后打印结果。");
        System.out.println("B executor.getCorePoolSize()="+executor.getCorePoolSize());
        System.out.println("B executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
        System.out.println("B executor.getPoolSize()="+executor.getPoolSize());
        System.out.println("B executor.getQueue().size()="+executor.getQueue().size());
        Thread.sleep(1000);
        System.out.println(" 1000 后打印结果。");
        System.out.println("C executor.getCorePoolSize()="+executor.getCorePoolSize());
        System.out.println("C executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
        System.out.println("C executor.getPoolSize()="+executor.getPoolSize());
        System.out.println("C executor.getQueue().size()="+executor.getQueue().size());
        Thread.sleep(10000);    //下面打印是验证销毁了 C-B
        System.out.println(" 10000 后打印结果。");
        System.out.println("D executor.getCorePoolSize()="+executor.getCorePoolSize());
        System.out.println("D executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
        System.out.println("D executor.getPoolSize()="+executor.getPoolSize());
        System.out.println("D executor.getQueue().size()="+executor.getQueue().size());
    }

}

运行结果如图:

ThreadPoolExecutor 线程池_第19张图片

9.验证(3.4)的情况 

package org.example.Executor;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class RunX_1 {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try{
                    System.out.println(Thread.currentThread().getName()+" run!"+System.currentTimeMillis());
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        ThreadPoolExecutor executor = new ThreadPoolExecutor(3,6,5, TimeUnit.SECONDS,new LinkedBlockingQueue<>(2));
        //使用 core 队列中的线程
        executor.execute(runnable);//1
        executor.execute(runnable);//2
        executor.execute(runnable);//3
        //使用第三方队列中的线程
        executor.execute(runnable);//1
        executor.execute(runnable);//2
        // C-B
        executor.execute(runnable);//1
        executor.execute(runnable);//2
        executor.execute(runnable);//3
        executor.execute(runnable);//4

        Thread.sleep(300);
        //可以看成 车中可载人的标准人数
        System.out.println("A executor.getCorePoolSize()="+executor.getCorePoolSize());
        //车中可载人的最大人数
        System.out.println("A executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
        //车中正在载的人数
        System.out.println("A executor.getPoolSize()="+executor.getPoolSize());
        //站在地面上等待被送的人数
        System.out.println("A executor.getQueue().size()="+executor.getQueue().size());
        Thread.sleep(800);
        System.out.println(" 800 后打印结果。");
        System.out.println("B executor.getCorePoolSize()="+executor.getCorePoolSize());
        System.out.println("B executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
        System.out.println("B executor.getPoolSize()="+executor.getPoolSize());
        System.out.println("B executor.getQueue().size()="+executor.getQueue().size());
        Thread.sleep(1000);
        System.out.println(" 1000 后打印结果。");
        System.out.println("C executor.getCorePoolSize()="+executor.getCorePoolSize());
        System.out.println("C executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
        System.out.println("C executor.getPoolSize()="+executor.getPoolSize());
        System.out.println("C executor.getQueue().size()="+executor.getQueue().size());
        Thread.sleep(10000);    //下面打印是验证销毁了 C-B
        System.out.println(" 10000 后打印结果。");
        System.out.println("D executor.getCorePoolSize()="+executor.getCorePoolSize());
        System.out.println("D executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
        System.out.println("D executor.getPoolSize()="+executor.getPoolSize());
        System.out.println("D executor.getQueue().size()="+executor.getQueue().size());
    }

}

运行结果如图,符合(3.4)的情况:

ThreadPoolExecutor 线程池_第20张图片

10.线程执行流程分析 

前面已经介绍过线程池在执行任务时的流程分析,在使用有参 new LinkedBlockingQueue(xxxx) 队列时,执行的流程时先判断 corePoolSize 大小够不够,如果不够向 new LinkedBlockingQueue(xxxx) 队列中存储,如果 new LinkedBlockingQueue(xxxx) 队列中放不下,则将剩余得到任务尝试向 C-B 中存放,如果 C-B 放不下就报异常。 


总结

通过使用线程池,我们更加知道了如何更加合理地分配线程资源,提高性能瓶颈。

你可能感兴趣的:(多线程,java,开发语言)