Java并发编程之线程池详解

这篇具有很好参考价值的文章主要介绍了Java并发编程之线程池详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

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

🐇一、简介

🐇二、相关代码

🐼1.线程池代码

🐼2.自定义实现线程池

🐇三、ThreadPoolExecutor类


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

Java并发编程之线程池详解,java,开发语言

🐇一、简介

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

🐇二、相关代码

🐼1.线程池代码

Java并发编程之线程池详解,java,开发语言

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

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

Java并发编程之线程池详解,java,开发语言

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

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

Java并发编程之线程池详解,java,开发语言

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

上述代码涉及到变量捕获

Java并发编程之线程池详解,java,开发语言

 这里的 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 没有被修改,所以可以被捕获。

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

Java并发编程之线程池详解,java,开发语言

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);
                }
            });
        }
    }
}

🐇三、ThreadPoolExecutor类

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

Java并发编程之线程池详解,java,开发语言

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

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

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

Java并发编程之线程池详解,java,开发语言

 第一个拒绝策略

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

第二个拒绝策略

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

第三个拒绝策略

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

第四个拒绝策略

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


 以上就是本篇博客的所有内容了,望有所帮助~文章来源地址https://www.toymoban.com/news/detail-663671.html

到了这里,关于Java并发编程之线程池详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Java 并发编程】一文读懂线程、协程、守护线程

    在 Java 线程的生命周期一文中提到了 就绪状态的线程在获得 CPU 时间片后变为运行中状态 ,否则就会在 可运行状态 或者 阻塞状态 ,那么系统是如何分配线程时间片以及实现线程的调度的呢?下面我们就来讲讲线程的调度策略。 线程调度是指系统为线程分配 CPU 执行时间片

    2023年04月08日
    浏览(55)
  • Java面试_并发编程_线程基础

    进程是正在运行程序的实例, 进程中包含了线程, 每个线程执行不同的任务 不同的进程使用不同的内存空间, 在当前进程下的所有线程可以共享内存空间 线程更轻量, 线程上下文切换成本一般上要比进程上下文切换低(上下文切换指的是从一个线程切换到另一个线程) 并发是单个

    2024年02月07日
    浏览(46)
  • 【Java并发编程】变量的线程安全分析

    1.成员变量和静态变量是否线程安全? 如果他们没有共享,则线程安全 如果被共享: 只有读操作,则线程安全 有写操作,则这段代码是临界区,需要考虑线程安全 2.局部变量是否线程安全 局部变量是线程安全的 当局部变量引用的对象则未必 如果给i对象没有逃离方法的作用

    2024年02月08日
    浏览(47)
  • java并发编程:多线程基础知识介绍

    最初的计算机只能接受一些特定的指令,用户每输入一个指令,计算机就做出一个操作。当用户在思考或者输入时,计算机就在等待。这样效率非常低下,在很多时候,计算机都处在等待状态。 后来有了 批处理操作系统 ,把一系列需要操作的指令写下来,形成一个清单,一次

    2024年02月07日
    浏览(48)
  • 【Java并发编程】线程中断机制(辅以常见案例)

    本文由浅入深介绍了中断机制、中断的常见案例和使用场景。 因为一些原因需要取消原本正在执行的线程。我们举几个栗子: 假设踢足球点球时,A队前4轮中了4个球,B队前4轮只中了2个球,此时胜负已分,第5轮这个点球就不用踢了,此时需要停止A队的线程和B队的线程(共

    2024年02月13日
    浏览(31)
  • Java并发编程学习18-线程池的使用(下)

    上篇介绍了 ThreadPoolExecutor 配置和扩展相关的信息,本篇开始将介绍递归算法的并行化。 还记得我们在《Java并发编程学习11-任务执行演示》中,对页面绘制程序进行一系列改进,这些改进大大地提供了页面绘制的并行性。 我们简单回顾下相关的改进过程: 第一次新增时,页

    2024年02月12日
    浏览(33)
  • Java并发编程学习笔记(一)线程的入门与创建

    认识 程序由指令和数据组成,简单来说,进程可以视为程序的一个实例 大部分程序可以同时运行多个实例进程,例如记事本、画图、浏览器等 少部分程序只能同时运行一个实例进程,例如QQ音乐、网易云音乐等 一个进程可以分为多个线程,线程为最小调度单位,进程则是作

    2024年02月16日
    浏览(47)
  • Java并发编程(三)线程同步 上[synchronized/volatile]

    当使用多个线程来访问同一个数据时,将会导致数据不准确,相互之间产生冲突,非常容易出现线程安全问题,比如多个线程都在操作同一数据,都打算修改商品库存,这样就会导致数据不一致的问题。 所以我们通过线程同步机制来保证线程安全,加入同步锁以避免在该线程没有完成

    2024年02月13日
    浏览(40)
  • Java并发编程学习16-线程池的使用(中)

    上篇分析了在使用任务执行框架时需要注意的各种情况,并简单介绍了如何正确调整线程池大小。 本篇将继续介绍对线程池进行配置与调优的一些方法,详细如下: ThreadPoolExecutor 为 Executors 中的 newCachedThreadPool 、 newFixedThreadPool 和 newScheduledThreadExecutor 等工厂方法返回的 Exe

    2024年02月10日
    浏览(39)
  • 【Java|多线程与高并发】线程池详解

    Java线程池是一种用于管理和重用线程的机制,它可以在需要执行任务时,从线程池中获取线程,执行任务,然后将线程放回池中,以便后续使用。线程池可以有效地管理线程的数量,提高程序的性能和资源利用率。 为什么从线程池里面取线程比直接创建线程快呢? 创建线程是

    2024年02月11日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包