栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

java线程池详解

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java线程池详解

目录

一、线程池的优势:二、线程池的架构图:三、Executors工具类中几个重点的创建线程池的方法:线程池的7个重要参数:

一、线程池的优势:


为何要用线程池,总的来说有以下几个原因:
总体来说,线程池有如下的优势:

(1)降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

(2)提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

(3)提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

二、线程池的架构图:

interface:接口

两个接口:
第一个接口Executor,它只定义了一个方法execute(Runnable command),相当于定义了一个框架,它能够执行一个任务。Executor就像是定义了一个框架,并确定了这个框架能够提供的功能。

第二个是ExecutorService接口,它继承Executor。主要扩展了关闭线程管理的一些功能,比如shutdown方法用来关闭线程池的任务,isTerminated方法来判断线程池的任务是否结束。

public interface ExecutorService extends Executor //继承了顶级接口Executor

另外ExecutorService还提供了最重要的方法submit,它支持了有返回结果的任务提交,也支持没返回值的任务提交,也引入了实现这个功能最关键的接口Callable、Future;

ExecutorService支持的比较广泛:
submit(Callable task):支持参数Callable,Callable是有返回值的接口
submit(Runnable task):支持参数Runnable ,Runnable是没有返回值的接口

Executor、ExecutorService都是接口(接口只定义方法,不实现方法),只是定义了提交任务、关闭任务等方法,相当于只是申明了线程池支持这些功能。

①:execute(Runnable command):执行行Ruannable类型的任务
②:submit(task):可用来提交Callable或Runnable任务,并返回代表此任务的Future对象
③:shutdown():温柔的关闭线程池,停止接受新任务,并执行完未完成的任务。
④:shutdownNow():强制关闭线程池,未完成的任务会以列表形式返回!
⑤:isTerminated():返回所有任务是否执行完毕。当调用shutdown()方法后,并且所有提交的任务完成后返回为true;当调用shutdownNow()方法后,成功停止后返回为true;
⑥:isShutdown():返回线程池是否关闭,当调用shutdown()或shutdownNow()方法后返回为true。

AbstractExecutorService抽象类:

public abstract class AbstractExecutorService implements ExecutorService

AbstractExecutorService是线程池系列第三个类,它是抽象类并继承ExecutorService接口。它主要是实现了ExecutorService接口的submit系列方法。

实现submit方法主要依靠RunnableFuture接口和它的实现类FutureTask。RunnableFuture继承Runnable和Future接口,而它的实现FutureTask的一个属性callable是Callable类型。

FutureTask实现了Runnable的run方法,run方法调用的是callable的call方法保存执行结果。同时也实现Future的get方法获取结果,如果任务还没有执行则阻塞线程。

所以submit方法实现的主要过程是提交的任务Callable封装成FutureTask,并把FutureTask当作Runnable丢给execute方法去异步执行,然后把FutureTask当作Future作为submit返回值。

但是AbstractExecutorService并没有实现execute方法,所以它是一个抽象类,在等待有缘人来实现execute,实现线程池的最后一步。

实现类ThreadPoolExecutor:
ThreadPoolExecutor是Executor框架的正真实现者。它实现了execute方法,execute真正的实现,以下三个关键点:

HashSet workers;//Worker集合,线程池

BlockingQueue workQueue;//阻塞队列,要执行的任务

final void runWorker(Worker w);//Worker调用这个方法,可以从阻塞队列中获取任务来执行。

线程池工具类:Executors:
类似于集合和集合工具类
数组和数据的工具类
线程和线程的工具类
Executors中有多个用于创建线程池的方法;

三、Executors工具类中几个重点的创建线程池的方法:

代码演示包含固定数量线程的线程池:

package com.fan.threadpool;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyThreadPoolDemo1 {
    public static void main(String[] args) {
        //使用线程的工具类Executors来创建线程池,固定数量的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        try {
            for (int i = 1; i <= 10; i++) {
                //execute方法用于执行任务
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+
                            "t 办理业务");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();//关闭线程池
        }
    }
}

代码演示包含一个线程的线程池:

package com.fan.threadpool;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyThreadPoolDemo1 {
    public static void main(String[] args) {
        //使用线程的工具类Executors来创建线程池,固定数量的线程池
        //一池包含5固定数量的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        //一池包含一个线程
        ExecutorService threadPool2 = Executors.newSingleThreadExecutor();
        try {
            //模拟10个用户来办理业务,每个用户就是一个来自外部的请求线程
            for (int i = 1; i <= 10; i++) {
                //execute方法用于执行任务
                

                threadPool2.execute(()->{
                    System.out.println(Thread.currentThread().getName()+
                            "t 办理业务");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
              //关闭线程池
            threadPool2.shutdown();//关闭线程池2
        }
    }
}

包含一个线程的线程池打印结果:


代码演示包含N个线程 的线程池:

package com.fan.threadpool;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyThreadPoolDemo1 {
    public static void main(String[] args) {
        //使用线程的工具类Executors来创建线程池,固定数量的线程池
        //一池5固定数量的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        //一池包含一个线程
        ExecutorService threadPool2 = Executors.newSingleThreadExecutor();
        //一池包含N个线程,N的数量根据任务量大小动态变化
        ExecutorService threadPool3 = Executors.newCachedThreadPool();
        try {
            //模拟10个用户来办理业务,每个用户就是一个来自外部的请求线程
            for (int i = 1; i <= 10; i++) {
                //execute方法用于执行任务
                

                threadPool3.execute(()->{
                    System.out.println(Thread.currentThread().getName()+
                            "t 办理业务");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
              //关闭线程池
            threadPool3.shutdown();//关闭线程池
        }
    }
}

打印:

或者这样:

package com.fan.threadpool;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class MyThreadPoolDemo1 {
    public static void main(String[] args) {
        //使用线程的工具类Executors来创建线程池,固定数量的线程池
        //一池5固定数量的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        //一池包含一个线程
        ExecutorService threadPool2 = Executors.newSingleThreadExecutor();
        //一池包含N个线程,N的数量根据任务量大小动态变化
        ExecutorService threadPool3 = Executors.newCachedThreadPool();
        try {
            //模拟10个用户来办理业务,每个用户就是一个来自外部的请求线程
            for (int i = 1; i <= 10; i++) {
                //execute方法用于执行任务
                

                threadPool3.execute(()->{
                    System.out.println(Thread.currentThread().getName()+
                            "t 办理业务");
                });
                //MILLISECONDS:/ˈmɪlisekənd/毫秒
                try { TimeUnit.MILLISECONDS.sleep(200);}
                catch (InterruptedException e) {e.printStackTrace();}
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
              //关闭线程池
            threadPool3.shutdown();//关闭线程池
        }
    }
}

打印结果:

创建线程池的方法解读:

线程池的7个重要参数:

从源码中可以看出,线程池的构造函数有7个参数,分别是corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。下面会对这7个参数一一解释。

一、corePoolSize 线程池核心线程大小:

线程池中会维护一个最小的线程数量,即使这些线程处于空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。

二、maximumPoolSize 线程池最大线程数量:

一个任务被提交到线程池以后,首先会找有没有 空闲存活线程,如果有则直接将任务交给这个空闲线程来执行,如果没有则会缓存到工作队列(后面会介绍)中。

三、keepAliveTime 空闲线程存活时间:(多余/空闲线程数 = 最大线程数 - 核心线程数)

当线程池中的现存线程数量大于corePoolSize的时 候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了keepAliveTime才会被销毁,最终会收缩到corePoolSize的大小。

四、unit 空闲线程存活时间单位:

keepAliveTime的计量单位

五、workQueue 工作阻塞队列:

用来保存等待被执行的任务的阻塞队列,且任务必须实现Runable接口,在JDK中提供了如下阻塞队列:
①:ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;
②:linkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene;
③:SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于 linkedBlockingQuene;
④:priorityBlockingQuene:具有优先级的无界阻塞队列;

六、threadFactory线程工厂。它是ThreadFactory类型的变量,用来创建新线程。默认使用 Executors.defaultThreadFactory() 来创建线程。

七、handler拒绝策略。线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:
①:AbortPolicy:直接抛出异常,默认策略;
②:CallerRunsPolicy:用调用者所在的线程来执行任务;
③:DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
④:DiscardPolicy:直接丢弃任务;

线程池监控API:

getActiveCount() :获取线程池中正在执行任务的线程数量
getCompletedTaskCount():获取已执行完毕的任务数
getTaskCount() :获取线程池已执行与未执行的任务总数
getPoolSize():获取线程池当前的线程数
getQueue().size():获取队列中的任务数


线程池工作原理

    在创建了线程池后,开始等待请求。

    当调用execute()方法添加一个请求任务时,线程池会做出如下判断:

如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务;如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列;如果这个时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;

    如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行。

    当一个线程完成任务时,它会从队列中取下一个任务来执行。

    当一个线程无事可做超过一定的时间(keepAliveTime)时,线程会判断:

如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到corePoolSize的大小。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/785645.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号