什么是线程池?

线程池顾名思义就是线程的容器,是用来管理线程的。

当我们在创建并实现线程时,如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。

而线程池的好处,就是可以方便的管理线程,也可以减少内存的消耗。

使用线程池的优点
  1. 降低资源消耗:通过重复利用已创建的线程降低线程和销毁带来的消耗。
  2. 提高响应速度:当任务到达时,任务可以不需要等待线程创建就能立即执行。
  3. 提高线程的可管理性:使用线程池可以统一进行线程分配、调度和监控。
线程池继承关系

Java 线程池隔离技术 java线程池管理_java

线程池的创建

可以通过ThreadPoolExecutor来创建一个线程:

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

其参数:

  1. corePoolSize:线程池的基本大小。
  2. maximumPoolSize:(线程池最大数量)线程池允许创建的最大线程数。
  3. keepAliveTime:(线程活动保持时间)线程池的工作线程空闲后,保持存活的时间。
  4. TimeUnit :线 程 活 动 保 持 时 间 的 单 位 。
  5. BlockingQueue:任务队列,用于保存等待执行的任务的阻塞队列。
  6. RejectedExecutionHandler :饱和策略。

其阻塞队列又分为五种:

  • ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按FIFO(先进先出)原则对元素进行排序。
  • LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO排序元素,吞吐量通常要高于ArrayBlockingQueue。
  • SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操 作 一 直 处 于 阻 塞 状 态 , 吞 吐 量 通 常 要 高 于 Linked-BlockingQueue 。
  • PriorityBlockingQueue:一个具有优先级的无限阻塞队列。

手工创建一个线程池:

ThreadPoolExecutor execotor = new ThreadPoolExecutor(
                2, 5, 1, TimeUnit.MINUTES,new LinkedBlockingDeque<>());
向线程池提交任务
execute()方法

execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功。

submit()方法

submit()方法用于提交需要返回值的任务。

如何使用?

在解释如何关闭线程池之后一起写一个实例展示如何使用…(如下)

关闭线程池

一旦shutdown()或者shutdownNow()执行完毕,线程池就进入TERMINATED状态,此时线程池就结束了。

  • shutdown()
    将线程池的状态设置为SHUTDOWN状态。然后中断所有没有正在执行任务的线程。
  • shutdownNow()
    将线程的状态设置为stop,然后尝试停止所有的正在执行暂停任务的线程,并返回等待执行任务的列表。
  • isShutdown()
    描述的是非RUNNING状态,也就是SHUTDOWN/ STOP/ TERMINATED三种状态。只要调用了shutdown()和shutdownNow()方法中的任何一个都会返回true。
  • isTerminating()
    描述的是SHUTDOWNSTOP两种状态。当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法才会返回true。
//关闭线程池
execotor.shutdown(); //方法一:安全的关闭
execotor.shutdownNow(); //方法二:立即关闭
System.out.println(execotor.isShutdown()); // true
System.out.println(execotor.isTerminating()); // true / false

向线程池提交任务和关闭线程池的实例:

package pool;

import java.util.concurrent.*;

public class TestThreadPool {
    public static void main(String[] args) {
        ThreadPoolExecutor execotor = new ThreadPoolExecutor(
                2, 5,1,TimeUnit.MINUTES,new LinkedBlockingDeque<>());

        // 设置线程运行三次
        for(int i=0; i<3; i++) {
            // Runnable
            // submit()方法实现
            execotor.submit(()-> System.out.println(Thread.currentThread().getName() + ":submit"));
            
            // execute()方法实现
            execotor.execute(()->{
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":execute");
            });

            // Callable
            // submit()方法实现,带返回值
            Future<String> future = execotor.submit(()->{
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return "hello";
            });
            try {
                System.out.println(future.get());
                System.out.println();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }

        //关闭线程池
        execotor.shutdown(); //安全的关闭
        execotor.shutdownNow(); //立即关闭
        System.out.println(execotor.isShutdown()); //true
        System.out.println(execotor.isTerminating());
    }
}

结果展示:

Java 线程池隔离技术 java线程池管理_Java 线程池隔离技术_02

Executor框架

其框架结构:

Java 线程池隔离技术 java线程池管理_System_03

ThreadPoolExecutor

Executor框架最核心的类是ThreadPoolExecutor,它是线程池的实现类。

  1. 创建无大小限制的线程池
public static ExecutorService
  1. 创建固定大小的线程池
public static ExecutorService new FixedThreadPool(int  nThreads)
  1. 单线程池
public static ExecutorService newSingleThreadExecutor()
FixedThreadPool

可重用固定线程数的线程池。

FixedThreadPool 使 用 无 界 队 列 LinkedBlockingQueue 作 为 线 程 池 的 工 作 队 列 ( 队 列 的 容 量 为Integer.MAX_VALUE)。
适用范围:
FixedThreadPool适用于为了满足资源管理的需求,而需要限制当前线程数量的应用场合,适用于负载比较重的服务器。
实例:

package pool;

import java.time.LocalDateTime;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class TestExecutor {
    public static void main(String[] args) {
        // 1.用户自定义线程工厂
        // 2.开发中建议使用自定义的线程工厂(阿里开发Java规约)
        ThreadFactory threadFactory = new ThreadFactory() {
            private final AtomicInteger id = new AtomicInteger(0);

            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r);
                thread.setName("Thread-Pool_Task-" + id.getAndIncrement());
                return thread;
            }
        };

        ExecutorService service = Executors.newFixedThreadPool(5,threadFactory);
        for (int i=0; i<10; i++) {
            service.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+ " " + LocalDateTime.now().toString());
                }
            });
        }

        if(!service.isTerminated()) {
            service.shutdown();
        }
    }
}
SingleThreadPoolExecutor

使用单个worker线程的Executor。

SingleThreadExecutor 的 corePoolSize 和 maximumPoolSize 被 设 置 为 1 。 其 他 参 数 与 FixedThreadPool 相 同 。SingleThreadExecutor 使 用 无 界 队 列 LinkedBlockingQueue 作 为 线 程 池 的 工 作 队 列 ( 队 列 的 容 量 为Integer.MAX_VALUE)。
适用范围:
SingleThreadExecutor适用于需要保证顺序地执行各个任务;并且在任意时间点,不会有多个线程是活动的应用场景。
实例:

package pool;

import java.time.LocalDateTime;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class TestExecutor {
    public static void main(String[] args) {
        ExecutorService service = Executors.newSingleThreadExecutor();
        for (int i=0; i<10; i++) {
            service.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }

        if (!service.isTerminated()) {
            service.shutdown();
        }
    }
}
CachedThreadPool

是一个会根据需要创建新线程的线程池

CachedThreadPool的corePoolSize被设置为0,即corePool为空maximumPoolSize被设置为Integer。MAX_VALUE,即maximumPool是无界的。这里把keepAliveTime设置为60L,意味着CachedThreadPool中的空闲线程等待新任务的最长时间为60秒,空闲线程超过60秒后将会被终止。
适用范围:
CachedThreadPool是大小无界的线程池,适用于执行很多的短期异步任务的小程序,或者负载较轻的服务器。
实例:

package pool;

import java.time.LocalDateTime;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class TestExecutor {
	public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < 10; j++) {
                        System.out.println(Thread.currentThread().getName()+"、"+j);
                    }
                }
            });
        }
        executorService.shutdown();
    }
}
ScheduledThreadPoolExecutor

是一个可以周期性执行任务的类(使用不多)

实例:

package pool;

import java.time.LocalDateTime;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class TestExecutor {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);

        // 周期性任务
        // 1.指定执行时间
        scheduledExecutorService.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "当前时间" + LocalDateTime.now());
            }
        }, 2, TimeUnit.MINUTES);

        // 指定周期性执行
        scheduledExecutorService.scheduleAtFixedRate(
                new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(Thread.currentThread().getName() + " 周期性 当前时间" + LocalDateTime.now());
                    }
                }, 0, 2, TimeUnit.SECONDS
        );
    }
}