太完整了!彻底搞懂线程池

一、创建线程方式

  • 继承Thread类
    • 当一个类继承自Thread类,那么该类就是一个线程类
  • 实现Runnable接口
    • 实现Runnable接口,屏蔽了继承Thread类的单继承局限性,实现了对象(线程任务对象、线程对象)的分离,提高了可扩展性,解耦
  • 实现Callable接口
    • 和Runnable同理,并且线程任务有返回值

二、线程和线程池性能对比

创建线程的固定步骤:创建、执行、销毁 首先系统创建一个线程的成本比较高,因为它涉及到与操作系统交互,当程序中需要创建大量生存期很短暂的线程时,频繁的创建和销毁线程对系统的资源消耗有可能大于业务处理,是对系统资源的消耗。针对这一情况,为了提高性能,我们就可以采用线程池。


太完整了!彻底搞懂线程池

三、线程池概述

线程池:好比水池,在该池子中存储很多线程 线程池在启动时,会创建大量的线程,当我们向线程池提交任务的时候,线程池就会启动一个线程来执行该任务,等待任务执行完毕后,线程并不会死亡,而且再次返回到线程池中,称为空闲状态,等待下一次任务的执行。

线程池是一种利用池化技术思想来实现的线程管理技术,主要是为了复用线程、便利地管理线程和任务,并将线程的创建和任务的执行解耦开来,我们可以创建线程池来复用已经创建的线程来降低频繁创建和销毁线程所带来的资源消耗。

线程池关系图

太完整了!彻底搞懂线程池



四、Executor&Executors&ExecutorService

1、Executor

Executor是线程池的顶级接口,其中execute方法用于提交线程任务,参数为Runnable 它是执行者接口,用于执行任务,Executor提供了execute()方法来执行已提交的Runnable任务的对象。

2、ExecutorService

ExecutorService是Executor的子接口,是Java提供的线程池,也就是说,每次我们需要使用线程的时候,可以通过ExecutorService获取线程,它可以有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞,同时提供定时执行、定期执行、单线程、并发数控制等功能,也不用使用TimerTask了。其中submit方法用于提交线程任务,参数为Callable java.util.concurrent.ExecutorService接口代表一种异步执行机制,它能够在后台执行任务。

比如:首先通过newFixedThreadPool()工厂方法创建一个ExecutorService的对象,这个方法创建了一个可以有具体个数的线程执行任务的线程池,然后Runnable接口的匿名实现类作为参数被传递给execute()方法,Runnable将会被ExecutorService中的一个线程来执行。execute方法是父接口Executor中的方法,该方法参数只能是Runnable对象 submit方法是子接口ExecutorService中的方法,该方法参数可以是Runnable对象,也可以是Callable对象。

3、Executors

JDK对线程池也进行了相关的实现,在真实企业开发中,我们也会很少去自定义线程池,而且使用JDK中自带的线程池。

Executors是个静态工厂类,它通过静态工厂方法返回ExecutorService、ThreadFactory和Callable等类的对象。我们可以使用Executors中所提供的静态方法来创建线程池,Executors用于创建线程池对象,ExecutorService用于控制(操作)线程池

线程池分类

方法名 说明
static  ExecutorsService  newCachedThreadPool() 缓存线程池
创建一个默认的线程池(可缓存线程池)
通过它的创建方式可以知道,创建的都是非核心线程,而且最大线程数为Integer的最大值,空闲线程存活时间是1分钟,如果有大量耗时的任务,则不适用该创建方式,它只适用于生命周期短的任务。
static  newFixedThreadPool(int  nThreads) 固定线程数线程池
创建一个指定最多线程数量的线程池
创建固定数量的可复用的线程数,来执行任务,当线程数达到最大核心线程数,则加入队列等待有空闲线程时再执行。
static  newSingleThreadExecutor() 单线程池
只用一个线程来执行任务,保证任务按照FIFO(先进先出)顺序一个个执行
static  newScheduledThreadPool(int  nThreads) 固定线程数,支持定时和周期性任务
创建并执行一个在给定时间内的定期操作

newCachedThreadPool线程池

源代码

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

代码示例

package cn.com.example13;


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

public class ExecutorsClass {

    /**
     * 创建一个默认的线程池
     */

    public static void newCachedThreadPoolHandler(){

        // 创建一个默认的线程池对象【最多可容纳int类型最大值】
        ExecutorService executorService = Executors.newCachedThreadPool();
        // Executors:创建线程池对象
        // ExecutorService:控制线程池

        // 提交线程任务
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"执行了");
            }
        });

        // 销毁线程【不建议】,当线程空闲时,会自动回收到线程池中等待
        executorService.shutdown();

    }
}

newFixedThreadPool线程池

创建指定上限的线程池 newFixedThreadPool的特点是他的核心线程数和最大线程数是一致的,并且是一个固定线程数的线程池。线程池的大小一旦达到最大值后,再有新的任务提交时则放入无界阻塞队列中,等到有线程空闲时,再从队列中取出任务继续执行。

newFixedThreadPool特点:

1.创建的线程数量固定
2.创建的线程可以重复使用
3.提交一个任务,就创建一个线程,直到达到线程池的最大容量
4.有执行异常结束的线程,线程池会补充一个新的线程
5.使用无边界的队列来存储需要执行的任务
6.使用完成,需要手动关闭线程池

源代码

public static ExecutorService new FixedThreadPool(int nThreads){
  return new ThreadPoolExecutor(nThreads,nThreads,0,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}

代码示例

package cn.com.example13;


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

public class ExecutorsClass {

    public static void newFixedThreadPoolHandler(){

        // 创建一个指定最多线程数量的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(20);

        /*
        调用的是父接口Executor中的execute方法提交线程任务
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"执行了");

            }
        });*/


        // 调用的是本接口的submit方法提交线程任务
        /*executorService.submit(new Callable<String>() {

            @Override
            public String call() throws Exception {
                System.out.println(Thread.currentThread().getName()+"执行了");
                return "";
            }
        });*/


        for (int i = 0; i <3 ; i++) {
            executorService.submit(new Callable<String>() {

                @Override
                public String call() throws Exception {
                    System.out.println(Thread.currentThread().getName()+"执行了");
                    return "";
                }
            });
        }

        executorService.shutdown();


    }

}

newSingleThreadExecutor

源代码

public static ExecutorService new SingleThreadExecutor(){
  return new FinalizabDelegatedExecutorService(new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>(Runnable)));
}

代码示例

package cn.com.example13;


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

public class ExecutorsClass {

    /**
     * 单线程
     */

    public static void newSingleThreadExecutorHandler(){
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i <3 ; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"执行了....");
                }
            });
        }

        executorService.shutdown();

    }


    public static void main(String[] args) {

        newSingleThreadExecutorHandler();

    }

}

newScheduledThreadPool

源代码

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE,
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
          new DelayedWorkQueue());
}

代码示例

package cn.com.example13;


import java.util.concurrent.*;

public class ExecutorsClass {

    /**
     * 固定线程,支持定时
     */

    public static void ScheduledThreadPoolExecutorHandler(){
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"执行了.....");
            }
        };

        // 参数一:任务对象
        // 参数二:延迟时间
        // 参数三:延迟类型
        // 5秒钟后执行任务对象
        scheduledExecutorService.schedule(runnable,5,TimeUnit.SECONDS);

        scheduledExecutorService.shutdown();
    }


    public static void main(String[] args) {

        ScheduledThreadPoolExecutorHandler();

    }

}

五、ThreadPoolExecutor

ThreadPoolExecutor类是ExecutorService接口的具体实现类,ThreadPoolExecutor使用线程池中的一个线程来执行给定的任务(Runnable或Callable) ThreadPoolExecutor内部的线程池包含不定数量的线程,池中线程的数量由corePoolSize(核心线程数量),maximumPoolSize(最大线程数量)这些变量来决定 当一个任务委托给线程池执行,此时如果池中线程数少于核心线程数量,即使池中有空闲的线程,但是还是会创建一个新的线程,如果任务队列是满的,核心线程数量个线程或者更多的切少于最大线程数量的线程正在运行,也会创建一个新的线程来执行任务。

ThreadPoolExecutor构造器

所有线程池最终都是通过这个方法来创建的。

线程池源码解析

// 可缓存线程池
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
}

// 固定线程池
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
}

// 单线程池
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(11,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
}

// 固定定时线程池
// 虽然new的是ScheduledThreadPoolExecutor,但是ScheduledThreadPoolExecutor是ThreadPoolExecutor的子类
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
}
// ScheduledThreadPoolExecutor继承ThreadPoolExecutor
public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService {}

ThreadPoolExecutor构造器

public ThreadPoolExecutor(int corePoolSIze,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable>  workQueue,ThreadFactory  threadFactory,RejectedExecutionHandler  handler);
public ThreadPoolExecutor(核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略);

参数 参数名称 说明 备注
参数一 int  corePoolSize 核心线程数量 不能小于0
参数二 int  maximumPoolSize 最大线程数 不能小于等于0,最大数量≥核心线程数量
参数三 long  keepAliveTime 空闲线程最大存活时间 不能小于0
参数四 TimeUnit  unit 时间单位 时间单位
参数五 BlockingQueueworkQueue 任务队列 不能为null
参数六 ThreadFactory  threadFactory 创建线程工厂 不能为null
参数七 RejectedExecutionHandler  handler 任务的拒绝策略 不能为null

参数详解

corePoolSize

线程池中核心的线程数量,类似于开发公司中核心程序员

maximumPoolSize

线程池中线程数量总数,最大线程数数量大于等于核心线程数

keepAliveTime 、TimeUnit

比如在双十一之前,处理订单需要20个线程,在双十一当天,需要100个线程,增加了80个线程,那么当过了双十一以后,12号,线程个数回归到之前20个,那么我们可以设置KeepAliveTime为1,TimeUnit为DAY,也就是存活一天

BlockingQueue

在双十一那天,100个线程如果不够用,那么我们可以先线程添加到队列中,进行排队,队列采用的是先进先出的数据结构

ThreadFactory

在双十一当天,线程从之前的20增加到100个,线程如果创建,那么通过ThreadFactory线程工厂获取

RejectedExecutionHandler

在双十一当天,如果线程个数从20增加到100,不够使用的话我们可以配置队列,但是当队列满了,我们可以配置RejectedExecutionHandler任务的拒绝策略,拒绝其他任务

代码示例

package cn.com.example13;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolExecutorClass {

    public static void threadPoolExecutorHandler(){

        // 核心线程数:10
        // 最大线程数:20
        // 任务队列:10
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10,20,0L, TimeUnit.MILLISECONDS,new ArrayBlockingQueue<Runnable>(10));

        for (int i = 1; i <=100 ; i++) {
            threadPoolExecutor.execute(new MyTask(i)); // 共100个任务,处理第31个任务时出现异常
        }

        threadPoolExecutor.shutdown();

    }


    public static void main(String[] args) {
        threadPoolExecutorHandler();
    }
}

class MyTask implements Runnable{

    public int i = 0;

    public MyTask(){}

    public MyTask(int i){
        this.i = i;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"---" + i);
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


太完整了!彻底搞懂线程池

为什么100个任务,处理第31个时出现异常?

太完整了!彻底搞懂线程池



为什么输入的结果,前面是1~10,中间是20~30,最后是10~20?

采用了提交优先级和执行优先级,并且submit底层也是调用了execute方法

提交优先级(submit):线程任务提交顺序:核心线程(1~10)、任务队列(11~20)、最大线程(21~30)

执行优先级(execute):线程任务执行顺序:核心线程(1~10)、最大线程(21~30)、任务队列(11~20)


太完整了!彻底搞懂线程池


原文始发于微信公众号(数字游民PG):太完整了!彻底搞懂线程池

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/174043.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!