当前位置: 首页 > article >正文

Java线程池—附阿里巴巴Java开发手册强制规范要求

文章目录

    • 一、线程池概述
    • 二、创建线程池
    • 三、线程池执行Runnable任务
    • 四、线程池执行Callable任务
    • 五、线程池工具类(Executors)—不推荐

一、线程池概述

线程池就是一个可以复用线程的技术
想象一下,如果不使用线程池会有什么问题?

假设:用户每次发起一个请求给后台,后台就创建一个新的线程来处理,下次新的任务过来肯定也会创建新的线程,如果用户量非常大,创建的线程也讲越来越多。然而,创建线程是开销很大的,并且请求过多时,会严重影响系统性能。

而使用线程池,就可以解决上面的问题。如下图所示,线程池内部会有一个容器,存储几个核心线程,假设有3个核心线程,这3个核心线程可以处理3个任务。

在这里插入图片描述

但是任务总有被执行完的时候,假设第1个线程的任务执行完了,那么第1个线程就空闲下来了,有新的任务时,空闲下来的第1个线程可以去执行其他任务。依此内推,这3个线程可以不断的复用,也可以执行很多个任务。
在这里插入图片描述
所以,线程池就是一个线程复用技术,它可以提高线程的利用率。

二、创建线程池

在JDK5版本中提供了代表线程池的接口ExecutorService,而这个接口下有一个实现类叫ThreadPoolExecutor类,使用ThreadPoolExecutor类就可以用来创建线程池对象。
下面是它的构造器如图所示:
在这里插入图片描述

常见的代码演示如下所示:

ExecutorService pool = new ThreadPoolExecutor(
    3,	//核心线程数有3个
    5,  //最大线程数有5个。   临时线程数=最大线程数-核心线程数=5-3=2
    8,	//临时线程存活的时间8秒。 意思是临时线程8秒没有任务执行,就会被销毁掉。
    TimeUnit.SECONDS,//时间单位(秒)
    new ArrayBlockingQueue<>(4), //任务阻塞队列,没有来得及执行的任务在,任务队列中等待
    Executors.defaultThreadFactory(), //用于创建线程的工厂对象
    new ThreadPoolExecutor.CallerRunsPolicy() //拒绝策略
);

关于线程池,需要注意下面的两个问题:

  • 临时线程什么时候创建?
    新任务提交时,发现核心线程都在忙、任务队列满了、并且还可以创建临时线程,此时会创建临时线程。
  • 什么时候开始拒绝新的任务?
    核心线程和临时线程都在忙、任务队列也满了、新任务过来时才会开始拒绝任务。

三、线程池执行Runnable任务

线程池执行的任务可以有两种,一种是Runnable任务;一种是callable任务。下面的execute方法可以用来执行Runnable任务。
在这里插入图片描述
先准备一个线程任务类

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        // 任务是干啥的?
        System.out.println(Thread.currentThread().getName() + " ==> 输出666~~");
        //为了模拟线程一直在执行,这里睡久一点
        try {
            Thread.sleep(Integer.MAX_VALUE);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

下面是执行Runnable任务的代码

ExecutorService pool = new ThreadPoolExecutor(
    3,	//核心线程数有3个
    5,  //最大线程数有5个。   临时线程数=最大线程数-核心线程数=5-3=2
    8,	//临时线程存活的时间8秒。 意思是临时线程8秒没有任务执行,就会被销毁掉。
    TimeUnit.SECONDS,//时间单位(秒)
    new ArrayBlockingQueue<>(4), //任务阻塞队列,没有来得及执行的任务在,任务队列中等待
    Executors.defaultThreadFactory(), //用于创建线程的工厂对象
    new ThreadPoolExecutor.CallerRunsPolicy() //拒绝策略
);

Runnable target = new MyRunnable();
pool.execute(target); // 线程池会自动创建一个新线程,自动处理这个任务,自动执行的!
pool.execute(target); // 线程池会自动创建一个新线程,自动处理这个任务,自动执行的!
pool.execute(target); // 线程池会自动创建一个新线程,自动处理这个任务,自动执行的!
//下面4个任务在任务队列里排队
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);

//下面2个任务,会被临时线程的创建时机了
pool.execute(target);
pool.execute(target);
// 到了新任务的拒绝时机了!
pool.execute(target);

在这里插入图片描述

四、线程池执行Callable任务

callable任务相对于Runnable任务来说,就是多了一个返回值。执行Callable任务需要用到下面的submit方法
在这里插入图片描述
先准备一个Callable线程任务

public class MyCallable implements Callable<String> {
    private int n;
    public MyCallable(int n) {
        this.n = n;
    }

    // 2、重写call方法
    @Override
    public String call() throws Exception {
        // 描述线程的任务,返回线程执行返回后的结果。
        // 需求:求1-n的和返回。
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sum += i;
        }
        return Thread.currentThread().getName() + "求出了1-" + n + "的和是:" + sum;
    }
}

再准备一个测试类,在测试类中创建线程池,并执行callable任务。

public class ThreadPoolTest2 {
    public static void main(String[] args) throws Exception {
        // 1、通过ThreadPoolExecutor创建一个线程池对象。
        ExecutorService pool = new ThreadPoolExecutor(
            3,
            5,
            8,
            TimeUnit.SECONDS, 
            new ArrayBlockingQueue<>(4),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.CallerRunsPolicy());

        // 2、使用线程处理Callable任务。
        Future<String> f1 = pool.submit(new MyCallable(100));
        Future<String> f2 = pool.submit(new MyCallable(200));
        Future<String> f3 = pool.submit(new MyCallable(300));
        Future<String> f4 = pool.submit(new MyCallable(400));

        // 3、执行完Callable任务后,需要获取返回结果。
        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());
        System.out.println(f4.get());
    }
}

五、线程池工具类(Executors)—不推荐

Java为开发者提供了一个创建线程池的工具类,叫做Executors,它提供了方法可以创建各种不能特点的线程池。如下图所示:
在这里插入图片描述
示例demo如下:

public class ThreadPoolTest3 {
    public static void main(String[] args) throws Exception {
        // 1、通过Executors创建一个线程池对象。
        ExecutorService pool = Executors.newFixedThreadPool(17);
        // 老师:核心线程数量到底配置多少呢???
        // 计算密集型的任务:核心线程数量 = CPU的核数 + 1
        // IO密集型的任务:核心线程数量 = CPU核数 * 2

        // 2、使用线程处理Callable任务。
        Future<String> f1 = pool.submit(new MyCallable(100));
        Future<String> f2 = pool.submit(new MyCallable(200));
        Future<String> f3 = pool.submit(new MyCallable(300));
        Future<String> f4 = pool.submit(new MyCallable(400));

        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());
        System.out.println(f4.get());
    }
}

Executors创建线程池这么好用,但是不推荐使用,为什么呢?原因在这里:看下图,这是《阿里巴巴Java开发手册》提供的强制规范要求。
在这里插入图片描述


http://www.kler.cn/a/162309.html

相关文章:

  • 红日靶机(七)笔记
  • 通用项目工程的过程视图概览
  • 学习方法——看差的书籍
  • 一文了解 Tableau 2024.3 如何展现已发布数据源的数据模型
  • 使用Python实现智能食品营养分析的深度学习模型
  • .netCore WebAPI中字符串加密与解密
  • 【CCF-B】中科院1区TOP,极速1天见刊,国人友好,基金申请必备之选!
  • navigationBar顶部导航栏,兼容适配所有机型(附完整案例)
  • 3接上篇 我的自定义GPTs的改进优化 与物理世界连接成功 GPTs的创建与使用定义和执行特定任务的功能模块 通过API与外部系统或服务的交互
  • git常用命令指南
  • android 13.0 去掉usb授权提示框 默认给予权限
  • 12月7日作业
  • pytorch学习入门之 Variable(变量)
  • 【面试经典150 | 二叉树】从前序与中序遍历序列构造二叉树
  • 苹果手机ios系统安装了一个免签应用书签webclip描述文件该如何卸载?
  • 学习php中使用composer下载安装firebase/php-jwt 以及调用方法
  • 细粒度视觉分类的注意内核编码网络
  • 美国Linux服务器的iptables防火墙介绍
  • 超详细介绍Ubuntu系统安装CUDA和cuDNN【一站式服务!!!】
  • 布匹瑕疵图像识别的CNN模型设计
  • 大数据的技术栈-逐步完善
  • 使用命令行移除VSAN中故障磁盘
  • kali linux入门及常用简单工具介绍(非常详细)从零基础入门到精通,看完这一篇就够了
  • SpringMVC 案例
  • 微前端个人理解与简单总结
  • 对Spring源码的学习:一