《Java并发编程实战》笔记3——线程池的使用

Java实现并发编程中的线程池管理! 🏆本文收录于「编程与技术实战」专栏,此专栏涵盖了C/C++编程、人工智能、数据结构、机器学习等技术领域的内容,助你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!! 阅读详情

1、线程饥饿死锁

在线程池中,如果任务依赖于其他任务,那么可能发生死锁。在单线程的Executor中,如果一个任务将另一个任务提交到同一个Executor,并且等待这个被提交任务的结果,那么通常会引发死锁。

如下面代码所示:

public class ThreadDeadlock {
        
	ExecutorService exec = Executors.newSingleThreadExecutor();//使用单线程的Executor

	public class LoadFileTask implements Callable<String> {
		private final String fileName;

		public LoadFileTask(String fileName) {
			this.fileName = fileName;
		}

		public String call() throws Exception {
			// Here's where we would actually read the file
			return "";
		}
	}

	public class RenderPageTask implements Callable<String> {
		public String call() throws Exception {
			Future<String> header, footer;
			header = exec.submit(new LoadFileTask("header.html"));
			footer = exec.submit(new LoadFileTask("footer.html"));
			String page = renderBody();
			System.out.println("提交子任务");
			// Will deadlock -- task waiting for result of subtask
			return header.get() + page + footer.get();
		}

		private String renderBody() {
			// Here's where we would actually render the page
			return "";
		}
	}

	public static void main(String[] args) {
		ThreadDeadlock deadLock = new ThreadDeadlock();
		ThreadDeadlock.RenderPageTask t = deadLock.new RenderPageTask();
		Future<String> result = deadLock.exec.submit(t);
		System.out.println("提交主任务");
		try {
			System.out.println("获取结果");
			result.get();
			System.out.println("===结束===");
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
	}
}

运行结果:

2、设置线程池的大小

如果线程池过大,大量的线程将在相对较少的CUP和内存资源上发生竞争,这不仅会导致更高的内存使用量,而且还可能耗尽资源。如果线程池过小,那么将导致许多空闲的处理器无法执行工作,从而降低吞吐率。

3、配置ThreadPoolExecutor

(1)参数简介

     ThreadPoolExecutor实现了ExecutorService和Executors接口,通常可通过Executors工厂方法来配置参数。较为完整的构造器如下所示。

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit timeUnit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler rejectedExecutionHandler)

corePoolSize:核心线程数(线程池努力去保持的一个线程数量)

maximumPoolSize:最大线程数

keepAliveTime:线程空闲时间。当线程数>corePoolSize,空闲线程最多等待keepAliveTime时间,便会销毁。

timeUnit :keepAliveTime的时间单位

workQueue:任务队列,保存等待执行的任务。

threadFactory :用来创建新线程的工厂

rejectedExecutionHandler :任务拒绝处理器。当拒绝任务时会触发

执行顺序:

①线程数<corePoolSize,即使有其他空闲的工作线程,也会创建一个新线程来处理请求。

②如果线程数>=corePoolSize,会将新任务放在队列中等待

③如果队列满了,且线程数<maximumPoolSize,才会创建一个新线程来处理请求。

④如果队列满了,且线程数>=maximumPoolSize,拒绝请求

如果将corePoolSize与maximumPoolSize设置成一样,那么会的到一个固定大小的线程池。

通过设置maximumPoolSize为一个无限大的数值(如Integer.MAX_VALUE),该线程池能适应任意数量的并发任务。

(2)管理队列任务

     ThreadPoolExecutor可以提供一个BlockingQueue来保存等待执行的任务。基本的任务排队方法有三种:无界队列、有界队列和同步移交。

     只有当任务相互独立时,为线程池或工作队列设置界限才是合理的。如果任务之间存在依赖性,那么有界的线程池或队列就可能导致线程“饥饿”死锁问题。此时应该使用无界的线程池,如:newCachedThreadPool。

(3)任务拒绝处理策略(RejectedExecutionHandler )

当任务添加到线程池中被拒绝时:

①ThreadPoolExecutor.AbortPolicy。会抛出RejectedExecutionException异常

②ThreadPoolExecutor.CallerRunsPolicy。将某些任务回退到调用者,从而降低新任务的流量。它不会在线程池的某个线程中执行新提交的任务,而是在一个调用了execute的线程中执行该任务。

③ThreadPoolExecutor.DiscardPolicy。无法执行的任务会被简单地抛弃

④ThreadPoolExecutor.DiscardOldestPolicy。如果executor没有关闭,在任务队列头的任务会被抛弃,然后会再次尝试重新提交新的任务(可能会再次失败,继续重复这个操作)

(4)线程工厂(ThreadFactory )

线程池需要创建新的线程时,都是通过线程工厂方法来完成的。但有时需要使用定制的线程工厂方法。

public interface ThreadFactory {
    Thread newThread(Runnable r);
}

/**
 * 自定义线程工厂
 */
public class MyThreadFactory implements ThreadFactory {
    private final String poolName;//线程池名

    public MyThreadFactory(String poolName) {
        this.poolName = poolName;
    }
    @Override
    public Thread newThread(Runnable runnable) {
        return new MyAppThread(runnable, poolName);
    }
}

 

public class MyAppThread extends Thread {
    public static final String DEFAULT_NAME = "MyAppThread";
    private static volatile boolean debugLifecycle = false;
    private static final AtomicInteger created = new AtomicInteger();
    private static final AtomicInteger alive = new AtomicInteger();
    private static final Logger log = Logger.getAnonymousLogger();

    public MyAppThread(Runnable r) {
        this(r, DEFAULT_NAME);
    }

    public MyAppThread(Runnable runnable, String name) {
        super(runnable, name + "-" + created.incrementAndGet());
        //设置日志显示级别
        log.setLevel(Level.FINE);
        //Set the handler invoked when this thread abruptly terminates due to an uncaught exception
        setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            public void uncaughtException(Thread t,
                                          Throwable e) {
                log.log(Level.SEVERE,
                        "UNCAUGHT in thread " + t.getName(), e);
            }
        });
    }

	public void run() {
        // Copy debug flag to ensure consistent value throughout.
        boolean debug = debugLifecycle;
        if (debug) log.log(Level.SEVERE, "Created " + getName());
        try {
            alive.incrementAndGet();
            super.run();
        } finally {
            alive.decrementAndGet();
            if (debug) log.log(Level.SEVERE, "Exiting " + getName());
        }
    }

    public static int getThreadsCreated() {
        return created.get();
    }

    public static int getThreadsAlive() {
        return alive.get();
    }

    public static boolean getDebug() {
        return debugLifecycle;
    }

    public static void setDebug(boolean b) {
        debugLifecycle = b;
    }
}

测试:

public class TestMain {
	public static void main(String[] args) {
		MyThreadFactory factory = new MyThreadFactory("Wow");
		MyAppThread.setDebug(true);
		MyAppThread t =(MyAppThread) factory.newThread(new Runnable() {
			@Override
			public void run() {
				int a = 1/0;//没有捕获的异常
			}
		});
		t.start();
		//*******************************************************************//
		MyThreadFactory factory2 = new MyThreadFactory("hoo");
		MyAppThread.setDebug(true);
		MyAppThread t2 =(MyAppThread) factory2.newThread(new Runnable() {
			@Override
			public void run() {
				int a = 1/0;//没有捕获的异常
			}
		});
		t2.start();
	}
}

测试结果:

 

 

(5)扩展ThreadPoolExecutor

可以根据需求扩展ThreadPoolExecutor重写beforeExecute、afterExecute、terminated等方法

java 四种线程池实例 执行一个异步任务你还只是如下new Thread吗?是不是太low 了一点? 我这里有四种线程池的案例轻松让你理解和使用线程池。 立即下载

相关推荐

《全连接到卷积神经网络:图像识别的演变历程》

这篇文章会从传统全连接层面临的困境说起,揭示卷积神经网络(CNN)成为图像识别核心动力的原因,探究其背后蕴含的设计思想与技术创新。但是回顾历史,CNN的出现提醒我们:技术的突破往往是由对自然规律的模仿和重新构建得来的。也许,当机器真的学会“像人一样看世界”的时候,它们的“眼睛”早就超过生物视觉的限制了。例如,用5×5的核处理224×224的输入时,只要25个参数(而不是全连接那种百万级的)。* 局部性:网络的底层只需留意局部的地方(像边缘、纹理这样的地方),不用管那些全局像素之间复杂的关系。

2401_82434295的博客 674

线程池问题

线程池问题

lgnorantpony的博客 1087

钣金设计高薪进阶:从材料工艺到DFMA降本增效全解析

钣金设计是机械制造领域的核心技术之一,它涉及将金属板材通过切割、折弯、焊接等工艺成型为所需零件。其核心原理在于深刻理解材料特性、制造工艺约束与结构力学,以确保设计在功能、成本与可制造性之间取得最佳平衡。这项技术的价值在于,优秀的钣金设计能直接驱动产品降本增效,通过优化材料利用率、简化装配工序和避免设计缺陷,为企业节省大量模具与生产成本。其应用场景广泛,从机箱机柜、汽车部件到电子设备外壳无处不在。本文将聚焦于实现这一价值的关键路径,深入探讨面向制造与装配的设计(DFMA)原则在钣金领域的实战应用,并剖析如何通

weixin_34301132的博客 392

线程饥饿死锁

1.饥饿死锁介绍 在线程池中,如果任务依赖于其他任务,那么可能产生死锁。在单线程Executor中,如果一个任务将另一个任务提交到同一个Executor,并且等待这个被提交任务的结果,那么通常会发生死锁。第二个任务停留在工作队列中,并等待第一个任务完成,而第一个任务又无法完成,因为它在等待第二个任务的完成。在更大的线程池中,如果所有正在执行任务的线程都由于等待其他仍处于工作队列中的任务而阻塞,那么...

diligence-zpf 438

PPPoe的抓包分析(详细)

PPPoe的报文结构 可以看出PPPoe的报文结构其实很简单,只不过是将PPPoe的报文塞到以太网的载荷中,而且它的头部字段也十分的简单,TLV就不说了,code字段表示了PPPoe不同的报文类型,比较常见的就是上面的五个报文了,PPPoe的报文类型其实还蛮多的。session ID主要的作用就是标识一段会话,因为使用PPPoe的最主要作用就是使得多台设备可以借助以太网多点接入多点访问的特点去和一台设备做上网认证请求,这样运营商一台认证设备就可以满足多个用户的认证请求,这和PPP相比起来好的不止一点。

qq_47529104的博客 7871

OpenClaw安装方式那么多,但建议大家还是用云厂商吧

OpenCloud的安装方式多样,包括One-liner、npm、pnpm和Hackable等。One-liner提供高效的单行命令安装;npm和pnpm作为Node.js包管理工具,分别提供常规和高性能的安装选项;Hackable则支持定制化安装。不同系统(macOS、Windows)和终端(PowerShell、CMD)的操作步骤各有特点,用户需根据自身技术背景选择合适的安装方式。官方推荐使用闲置计算资源或云部署方案以获得更佳体验。

涛涛讲AI 1401

Java线程池介绍及使用(这一篇就够了)

1. Java线程池介绍及使用 2. 线程池参数说明 3. 线程池执行流程详解 4. ThreadPoolExecutor实际应用 5. 线程池避免踩坑

Mr.绵羊的知识星球 6419

用STM32F103C8T6和TB6612驱动模块,从零搭建一辆能避障循迹的智能小车(附完整代码)

本文详细介绍了如何使用STM32F103C8T6和TB6612驱动模块从零搭建一辆能避障循迹的智能小车。内容涵盖硬件选型、电路设计、底层驱动配置以及智能行为算法实现,并附有完整代码示例,帮助开发者快速掌握嵌入式开发的核心技术。

weixin_42641741的博客 446

SingleThreadExecutor(单线程执行器)

前要:一般的执行器会为每个任务单独创建线程,起码是分配单独的线程,即每个任务有其自己的线程。这样可以让任务并发执行。 问题:既然这样,为什么只用一个线程处理多个任务呢? 如果是这样,那一个线程某一时刻只处理一个任务。这样有什么好处呢?我们有讲到过,线程难就难在协调不同线程驱动的任务之间对内存和IO资源的使用,以使得这些资源不会同时被多个任务访问。 应用场景:当多...

dieqiuxie4160的博客 564

零售-公司间关联交易退货STO的实现方式

零售-公司间关联交易采购 STO-退货 集团公司下经常会出现一家公司和另外一家公司的公司间的关联交易,如跨公司销售、总分公司之间采购,分公司之间的采购等业务;通常公司间采购会采取STO的模式来实现(根据业务模式可以将库存转储统一做成STO的模式,公司间STO、同公司工厂间、同工厂库存地点间 的STO SAP都是支持的); 公司间STO 正向A向B采购都很熟悉,公司间退货STO的其实也是比较普遍的业务现象,很多实施着也将其做成反向的STO(B向A采购),而不是退货STO(A向B,item 选择return的标

weixin_45518759的博客 2835

基于STM32与ESP8266的MQTT协议实现阿里云物联网平台远程LED控制

本文详细介绍了如何利用STM32单片机与ESP8266 Wi-Fi模块,通过MQTT协议连接阿里云物联网平台,实现远程控制LED灯的完整物联网项目。内容涵盖硬件连接、阿里云平台配置、MQTT协议与AT指令解析、STM32代码实战以及数据解析与调试技巧,为嵌入式开发者提供了一个清晰、实用的物联网入门实践指南。

svm4gardener的博客 792

Executors.newSingleThreadScheduledExecutor()实现定时任务 并解决其发生异常时卡死状态

最近工作中用Executors.newSingleThreadScheduledExecutor()实现了一个定时任务 该任务会在一次任务执行完毕后的间隔时间才会执行下一次任务 import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.ut...

w772397262的博客 2万+

FFMPEG音视频开发: Linux下采集音频(alsa-lib库)、视频(V4L2框架)数据编码并实时推流到RTMP流媒体服务器,达到直播功能(推流)

一、环境介绍 操作系统: VM虚拟机运行的ubuntu18.04 FFMPEG版本: 4.4.2 摄像头: 罗技USB摄像头、电脑自带摄像头 声卡:电脑自带声卡 二、FFMPEG、X264的安装 参考这里:FFMPEG开发: Linux下采集摄像头数据录制成MP4视频保存到本: 地https://blog.youkuaiyun.com/xiaolong1126626497/article/d...

2990

[笔记][Java7并发编程实战手册]4.3 创建固定的线程执行器newFixedThreadPool线程池

[笔记][Java7并发编程实战手册]系列目录简介 newFixedThreadPool(int nThreads, ThreadFactory threadFactory) 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程,在需要时使用提供的 ThreadFactory 创建新线程。 newCachedThreadPool()创建的线程池的特性是:自动回收不使用

代码有毒的博客 5939

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

深入理解 Java 中的并发编程:线程池与锁的使用

并发编程是指在同一时间段内,多个任务或操作可以同时执行的编程方法。并发编程能够显著提高应用程序的性能,尤其是在处理大量任务或需要高响应速度的场景下。在 Java 中,线程是进行并发编程的基本单位。Java 提供了丰富的 API 来管理线程,处理同步和并发问题。常见的并发问题包括资源竞争、死锁、线程安全等。线程池是一个包含多个线程的池子,可以在任务需要时从池子中获取线程,执行任务后返回线程池,以便再次使用。在 Java 中,线程池和锁是并发编程中非常重要的两个概念。

一名热衷于技术的全栈开发者,专注于前端与后端的全面技术探索。在这里,我将分享我在技术领域的学习与成长,助力更多开发者的进步。 1512

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

[Java进阶] 并发编程实战—线程&线程池&协程

本文主要介绍了Java并发编程中的线程、线程池和协程的多种创建方式,对于每一种方式都给出了详细的代码案例。通过代码案例可以很容易的理解在真实的项目开发中如何使用线程、线程池以及协程。

MCC_MCC_MCC的博客 1615

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

【Java并发编程实战】

本篇是阅读《Java 并发编程实战》的零碎笔记,整体非常凌乱,并不适合阅读,仅做个人备忘。

猫吻鱼的博客 1475

黑马Java面试笔记之 并发编程篇(线程池+使用场景)

核心线程数目 - 池中会保留的最多线程数最大线程数目 - 核心线程+救急线程的最大数目生存时间 - 救急线程的生存时间,生存时间内没有新任务,此线程资源会释放unit时间单位 - 救急线程的生存时间单位,如秒、毫秒等workQueue- 当没有空闲核心线程时,新来任务会加入到此队列排队,队列满会创建救急线程执行任务线程工厂 - 可以定制线程对象的创建,例如设置线程名字、是否是守护线程等handler拒绝策略 - 当所有线程都在繁忙,workQueue 也放满时,会触发拒绝策略拒绝策略有4种。

2201_75550069的博客 1232

《Java 并发编程》线程池

《Java 并发编程》线程池🚀1. 自定义线程池🚀2. ThreadPoolExecutor🚁2.1 线程池状态🚁2.2 构造方法🚁2.3 newFiexedThreadPool🚁2.4 newCachedThreadPool🚁2.5 newSingleThreadExecutor🚀3. 提交任务🚀4. 关闭线程池🚀5. 任务调度线程池 🚀1. 自定义线程池 阻塞队列中维护了由主线程(或者其他线程)所产生的的任务 主线程类似于生产者,产生任务并放入阻塞队列中 线程池类似于消费者,得到阻塞队列中已有的任务

华仔仔的博客 2596

线程池:Java并发编程的加速器

线程池通过复用线程,显著减少了线程创建和销毁的开销,提高了资源利用率和任务的响应速度。它允许预先创建一组线程,并在需要时从池中取出线程来执行任务,任务完成后线程返回池中等待下一个任务。

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 5113

Java并发编程——线程池

线程池原理剖析、使用线程池的好处、Executor框架详解

m0_62909831的博客 1120

Java并发编程面试必备:如何创建线程池、线程池拒绝策略

本文介绍了Java中的线程池及其在高并发编程中的应用,重点解析了ThreadPoolExecutor的核心参数配置、线程池类型选择及适用场景。文章探讨了常见的线程池拒绝策略,包括AbortPolicy和CallerRunsPolicy,并分析了它们在应对系统过载时的效果。此外,文章提供了20个常见面试问题及其详尽解答,涵盖了线程池的工作机制、任务队列、线程复用、异常处理与监控等关键知识点,为面试者提供了全面的复习指导,以帮助其在并发编程面试中表现出色。

程序员-杨胡广的博客 2261

Java并发编程面试题: 线程池

收集大量Java经典面试题目,内容涵盖了包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 等知识点。适合准备Java面试的读者参考和复习。

m0_46013789的博客 1602

Java 并发编程下篇 -(线程池)

七、线程池

雾里 4084

并发编程-线程池原理与实战

AbstractExecutorService是一个抽象类,它实现了ExecutorService接口。AbstractExecutorService存在的目的是为ExecutorService中的接口提供默认实现。//......

洛晓关的博客 1205
上一篇: 《Java并发编程实战》笔记2——取消与关闭
下一篇: 《Java并发编程实战》笔记4——避免活跃性危险
ack_Finding
博客等级 码龄10年 6粉丝 71原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值