JUC线程进阶篇09:线程池

JUC线程进阶篇09:线程池

标签: 多线程

了解线程池

为什么使用线程池

线程池提供了一个线程队列,队列中保存着所有等待状态的线程。合理利用线程池能够带来三个好处:
1. 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
2. 提高响应速度:当任务到达时,任务可以不需要的等到线程创建就能立即执行。
3. 提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

线程池工作流程

当提交一个新任务到线程池时,线程池的处理流程如下:

  1. 首先线程池判断基本线程池是否已满?没满,创建一个工作线程来执行任务。满了,则进入2。
  2. 其次线程池判断工作队列是否已满?没满,则将新提交的任务存储在工作队列里。满了,则进3。
  3. 最后线程池判断整个线程池是否已满?没满,则创建一个新的工作线程来执行任务,满了,则交给饱和策略来处理这个任务。

线程池体系结构

java中的线程池是通过Executor框架实现的,Executor 框架包括类:ExecutorExecutorsExecutorServiceThreadPoolExecutorCallableFutureFutureTask的使用等。

Executor主要结构

Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService

java.util.concurrent.Executor:负责线程的使用与调度的根接口
    |-- ExecutorService子接口:增加Executor的行为,是Executor实现类的最直接接口
        |-- ThreadPoolExecutor:线程池实现类
        |-- ScheduledExecutorService子接口:负责线程池的调度
            |-- ScheduledThreadPoolExecutor实现类:继承了ThreadPoolExecutor类,实现了ScheduledExecutorService接口

ThreadPoolExecutor创建线程池

我们可以通过ThreadPoolExecutor来创建一个线程池。

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

这个构造方法有7个参数:

  • corePoolSize:池中所保存的线程数,包括空闲线程。
  • maximumPoolSize-池中允许的最大线程数。
  • keepAliveTime – 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
  • unit – keepAliveTime 参数的时间单位。
  • workQueue – 执行前用于保持任务的队列。此队列仅保持由execute方法提交的 Runnable任务。
  • threadFactory – 执行程序创建新线程时使用的工厂。
  • handler – 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。

在JDK API文档中,有如此一段话:

强烈建议程序员使用较为方便的Executors工厂方法Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)、Executors.newFixedThreadPool(int)(固定大小线程池)Executors.newSingleThreadExecutor()(单个后台线程)它们均为大多数使用场景预定义了设置。

Executors工具类创建线程池

要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors工具类中提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService 接口:

  1. ExectorService newFixedThreadPool():创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

  2. ExectorService newCachedThreadPool():缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量

3.ExectorService newSingleThreadExecutor():创建单个线程池,这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

  1. ScheduledExectorService newScheduledThreadPool():创建固定大小的线程池,可以延迟或定时地执行任务

线程池的使用

线程池的使用分为3步:
1. 创建线程池
2. 为线程池中的线程分配任务
3. 关闭线程池

重要方法

在ThreadPoolExecutor类中有几个非常重要的方法:

execute() submit() shutdown() shutdownNow()
  • execute()方法实际上是Executor中声明的方法,在ThreadPoolExecutor进行了具体的实现,这个方法是ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行

  • submit()方法是在ExecutorService中声明的方法,在AbstractExecutorService就已经有了具体的实现,在ThreadPoolExecutor中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,它能够返回任务执行的结果,去看submit()方法的实现,会发现它实际上还是调用的execute()方法,只不过它利用了Future对象的的future.get()方法来获取任务执行结果。

  • shutdown()shutdownNow()是用来关闭线程池的,一个较平稳,一个立即关闭。

代码演示

使用Runnable方法创建线程:

public class TestThreadPool {
    public static void main(String[] args) {
        // 1.创建线程池
        ExecutorService pool = Executors.newFixedThreadPool(5);//创建了5个线程的线程池
        ThreadPoolDemo td = new ThreadPoolDemo();   //创建一个线程任务
        // 2.为线程池中的线程分配任务
        for (int i = 0; i < 10; i++) {
            pool.submit(td);
        }

        // 3.关闭线程池
        pool.shutdown();
    }
}
class ThreadPoolDemo implements Runnable{
    private int i = 0;
    @Override
    public void run() {
        while (i<= 5) {
            System.out.println(Thread.currentThread().getName()+":"+i++);
        }

    }
}

使用Callable创建线程的方式,有返回值,使用Future获取:

public class TestThreadPool {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // 1.创建线程池
        ExecutorService pool = Executors.newFixedThreadPool(5);//创建了5个线程的线程池

        List<Future<Integer>> list = new ArrayList<Future<Integer>>();//Future的队列,用于获取多个Future对象
        for (int i = 0; i < 10; i++) {
            Future<Integer> future = pool.submit(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    int sum = 0;
                    for (int i = 0; i < 100; i++) {
                        sum +=i;
                    }
                    return sum;
                }
            });
            list.add(future);
        }
        // 3.关闭线程池
        pool.shutdown();
        for (Future<Integer> future: list) {
            System.out.println(future.get());
        }
    }
}

线程调度

在线程池中提到了一个ScheduledExecutorService这一个类就是用来实现线程的调度的。

public class TestScheduledThread {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // 使用newScheduledThreadPool创建五个可调度的线程的线程池
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);

        for (int i = 0; i < 5; i++) {
            // schedule有三个参数:分配的任务、延时时间、单位
            ScheduledFuture<Integer> result = pool.schedule(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    int num = new Random().nextInt(100);//产生随机数
                    System.out.println(Thread.currentThread().getName()+":"+num);
                    return num;
                }
            }, 3, TimeUnit.SECONDS);

            System.out.println(result.get());
        }
        pool.shutdown();

    }
}
    原文作者:JUC
    原文地址: https://blog.csdn.net/japson_iot/article/details/80465432
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞