Java并发编程之线程池详解

目录

🐳今日良言:不悲伤 不彷徨 有风听风 有雨看雨

🐇一、简介

🐇二、相关代码

🐼1.线程池代码

🐼2.自定义实现线程池

🐇三、ThreadPoolExecutor类


🐳今日良言:不悲伤 不彷徨 有风听风 有雨看雨

🐇一、简介

首先来介绍一下什么是线程池,线程池是一种利用池化技术思想来实现的线程管理技术,主要是为了复用线程、便利地管理线程和任务并将线程的创建和任务的执行解耦开来。我们可以创建线程池来复用已经创建的线程来降低频繁创建和销毁线程所带来的资源消耗。在JAVA中主要是通过java.util.concurrent包中的ThreadPoolExecutor类来实现线程池 。

🐇二、相关代码

🐼1.线程池代码

这里就是创造出一个10个线程的线程池,然后就可以随机安排这些线程完成任务了。

线程池提供了一个重要的方法 submit 可以给线程池提交若干个任务。

 submit的参数是一个Runnable,用来描述这些线程要执行的任务是什么。

线程池中的线程都是前台线程,前台线程会阻止进程结束,也就是说,运行程序之后,main线程结束了,但是整个进程没有结束。

当前是往线程池里放了 1000 个任务,1000 个任务就是由这 10 个线程来平均分配一下,差不多是一人执行 100个,但是这里并非是严格的平均,可能有的多一个有的少一个都正常。(每个线程都执行完一个任务之后,再立即取下一个任务...由于每个任务执行时间都差不多,因此每个线程做的任务数量就差不多)

上述代码涉及到变量捕获

 这里的 i 是主线程里的局部变量(在主线程的栈上),随着主线程的代码执行结束就销毁了,但是,很可能这里的 for 循环已经执行完了,当前 run 的任务在线程池里还没有排到,此时 i 就已经要销毁了。这里的 run 方法属于 Runnable ,这个方法的执行时机并不是立刻执行,而是在未来的某个时间点(后续在线程池的队列中排到了) 才会执行,为了避免作用域的差异,导致执行 run 方法的时候,i 已经销毁了,于是就有了变量捕获,也就是让 run 方法把主线程的 i 往当前 run 方法的栈上拷贝一份(在定义 run 方法的时候了,把当前 i 的值记住,后续执行 run 的时候,就创建一个也叫做 i 的局部变量,并且把这个值给赋值过去)。

在Java 中,对于变量捕获,做了一些额外的要求,在JDK 1.8之前,要求变量捕获只能捕获 final 修饰的变量,后来发现,这样太麻烦了,于是,在 JDK 1.8 开始,发送了一点标准,要求不一定非得待 final 关键字,只要代码中没有修改过这个变量,也可以捕获。

在上述代码中,i 是被修改的,因此不能捕获,但是 n 没有被修改,所以可以被捕获。

接下来,介绍一下几种不同的线程池:

new FixedThreadPool        创建固定线程数的线程池。

newCachedThreadPool      线程数量是动态变化的,任务多了就多创建几个线程,任务少了 

                                            就少创建几个。

newScheduledThreadPool  类似于定时器,让任务延时执行。

newSingleThreadExecutor  线程池里只有一个线程。

上述这些线程池,本质上都是通过包装 ThreadPoolExecutor 来实现的,这个线程池用起来比较麻烦,所以提供了工厂类,让我们使用更方便,ThreadPoolExecutor 提供的功能更为强大,后面会详细介绍。 

🐼2.自定义实现线程池

 自定义实现线程池代码如下:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

class MyThreadPool {
    // 阻塞队列
    private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
    // 若干个工作线程
    // n表示线程的数量
    public MyThreadPool(int n) {
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(() -> {
                while (true) {
                    try {
                        // 从阻塞队列中取出然后执行
                        Runnable runnable = queue.take();
                        runnable.run();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
            t.start();
        }
    }
    // 注册任务给线程池
    public void submit(Runnable runnable) {
        try {
            queue.put(runnable);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
public class Exercise{
    public static void main(String[] args) {
        MyThreadPool myThreadPool = new MyThreadPool(10);
        for (int i = 0; i < 1000; i++) {
            int n = i;
            myThreadPool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello:"+n);
                }
            });
        }
    }
}

使用线程池的好处主要有:

1)降低系统资源开销

        线程池通过服用线程来减少创建线程和销毁现场的资源开销,可以提高系统资源利用率。

2)提高响应速度

        在线程池中,线程可以迅速获取并执行任务,并不需要等待线程的创建,这可以大大提高系统的响应速度。

3)控制并发线程数量

        线程池可以限制同时执行的线程的数量,有效地避免了因过多线程导致的系统资源耗尽或性能下降的问题。

4)提高系统稳定性

        通过同意管理线程的生命周期,线程池可以有效地避免因线程失控导致的系统崩溃,提高系统的稳定性。

5)提供更多功能

        线程池提供了如定期执行、定时执行、单线程等功能的线程,使线程的使用更加灵活和方便。

🐇三、ThreadPoolExecutor类

最后,介绍一下ThreadPoolExecutor 里面的参数,如下图:

1)int corePoolSize

核心线程数

2)int maximumPoolSize

最大线程数

ThreadPoolExecutor 相当于是把里面的线程分成了两类,一类是核心线程,一类是临时线程,核心线程相当于是正式工,临时线程相当于是临时工,这二者之和就是最大线程数。

如果任务多,需要创建更多的线程,但是,一个程序的任务不一定始终都有很多,有时候多,有时候少,如果现在任务少了,线程还那么多,就非常不合适了,因此,就需要对现有的线程进行一定的淘汰,整体的淘汰策略是:核心线程保底,临时线程动态调节。

在实际开发的时候,线程池的线程数设置成多少比较合适呢?

实际上,这里不应该回答出具体的数字,在实际开发中,线程池的线程数设置需要根据具体情况进行调整,一般来说,应该设置为CPU核心数的两倍到四倍之间,如果线程数过少,则导致任务等待时间过长,而如果线程数过多,会导致系统资源浪费。如果任务是IO密集型的,那么可以适当的增加线程数,如果任务是CPU密集型的,则可以适当的减少线程数。

3)long keepAliveTime

临时线程的最大空闲时间,超出这个时间,临时线程就会被销毁了。

也就是临时工的最大摸鱼时间。

4)TimeUnit unit 

时间单位(s,ms,分钟...)

5)BlockingQueue<Runnable> workQueue

线程池的任务队列

此处使用的是阻塞队列,每个线程都是在不停的尝试take,如果有任务,就take成功,没有就阻塞。

6)ThreadFactory  threadFactory

用于创建线程,线程池是需要创建线程的

7)RejectedExecutionHandler handler

描述了线程池的“拒绝策略”,也是一个特殊的对象,描述了当线程池任务队列满了,如果继续添加任务会有什么样的行为。

标准库提供了四个拒绝策略,如下图:

 第一个拒绝策略AbortPolicy(线程池默认的拒绝策略)

如果任务太多,任务队列满了,就直接抛出异常。

第二个拒绝策略CallerRunsPolicy

如果任务太多,任务队列满了,多出来的任务,谁加的谁负责执行。

第三个拒绝策略DiscardOldestPolicy

如果任务太多,任务队列满了,丢弃最早的任务。

第四个拒绝策略DiscardPolicy

如果任务太多,任务队列满了,丢弃最新的任务。


 以上就是本篇博客的所有内容了,望有所帮助~

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java多线程线程池是一种重用线程的机制,它可以管理和调度多个线程来执行任务。使用线程池可以提高程序的性能和效率,同时也可以避免线程的频繁创建和销毁。 在Java中,可以使用线程池类ExecutorService和Executors来创建和管理线程池。具体步骤如下: 1. 创建一个线程池对象:可以使用Executors提供的静态方法创建不同类型的线程池,比如newFixedThreadPool固定大小线程池、newCachedThreadPool缓存线程池等。 2. 向线程池提交任务:使用submit方法向线程池提交需要执行的任务,也可以使用execute方法提交任务。任务可以是实现了Runnable接口或Callable接口的类,也可以是Lambda表达式。 3. 线程池执行任务:线程池会根据线程池的规模和任务的数量来调度和执行任务,多个任务会并发执行。 4. 关闭线程池:当不再需要线程池时,可以调用线程池的shutdown方法来关闭线程池,确保所有的任务都被执行完毕。 使用线程池的好处有: 1. 提高性能:线程池可以重用线程,避免线程频繁创建和销毁的开销,提高程序的性能。 2. 提供线程管理和调度:线程池可以管理和调度线程,根据线程池的规模和任务的数量来调度和执行任务。 3. 控制资源的使用:线程池可以限制并发线程的数量,避免过度占用系统资源。 在Java开发中,使用线程池是一种推荐的多线程编程方式,也是阿里巴巴在其《Java开发手册》中强制规定的做法。 Java线程的创建是依赖于系统内核的,通过JVM调用系统库创建内核线程,内核线程与Java Thread是1:1的映射关系。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Java多线程(六):线程池详解](https://blog.csdn.net/m0_59140023/article/details/124436385)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [java多线程和线程池](https://blog.csdn.net/qq_29996285/article/details/118955325)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值