Java 多线程补充
线程池
Java线程池是一种能够有效管理线程资源的机制,它可以显著提高应用性能并降低资源消耗。
线程池的主要优点包括:
- 资源利用高效:通过重用已存在的线程,减少了频繁创建和销毁线程带来的系统开销。
- 响应速度提升:任务到来时可以迅速被执行,而不必等待新线程的创建。
- 管理监控便捷:线程数量有限,避免无限制创建线程导致的资源耗尽和系统不稳定问题,同时便于统一分配、调优和监控。
- 功能丰富强大:提供了多种类型的线程池,如定时、定期以及可控线程数的线程池,满足不同的业务需求。
在使用线程池时,需要注意以下几点:
- 合理配置参数:创建线程池时需要根据实际需求合理设置线程数量等参数,以避免资源浪费或系统过载。
- 预防潜在风险:使用
Executors
类中的便捷方法虽然简单,但可能会隐藏复杂性,如不当使用可能导致内存溢出(OOM)或线程耗尽等问题。 - 选择正确任务类型:理解
Runnable
和Callable
接口的区别,并根据任务的特性选择合适的类型提交给线程池执行。
Executors
Java Executors是一个用于创建线程池的工厂类,它提供了一系列的静态工厂方法来简化线程池的创建和管理。
Executors类中提供的方法包括:
newCachedThreadPool()
:创建一个可缓存的线程池,适用于执行大量的短期异步任务。线程数量可以根据需要自动扩展,如果有可用的空闲线程,就会重用它们;如果没有可用的线程,就会创建一个新线程。newFixedThreadPool(int nThreads)
:创建一个固定大小的线程池,适用于执行固定数量的长期任务。线程数量是固定的,不会自动扩展。newSingleThreadExecutor()
:创建一个单线程的线程池,适用于需要按顺序执行任务的场景。newScheduledThreadPool(int corePoolSize)
:创建一个固定大小的线程池,用于定时执行任务。线程数量固定,不会自动扩展。
使用Executors的优点包括:
- 简化线程管理:Executors通过提供工厂方法,隐藏了线程池的复杂性,使得线程池的创建变得简单快捷。
- 适应不同场景:根据不同的业务需求,可以选择不同类型的线程池,如固定大小、单线程或定时执行等。
- 提高性能:通过复用线程,降低了资源消耗,提高了系统的响应速度和吞吐量。
以下是Java Executors的代码实现示例:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ExecutorsExample {public static void main(String[] args) {// 创建一个可缓存的线程池ExecutorService executorService = Executors.newCachedThreadPool();// 提交任务到线程池for (int i = 0; i < 10; i++) {executorService.execute(new Task());}// 关闭线程池executorService.shutdown();}static class Task implements Runnable {@Overridepublic void run() {System.out.println("Task executed by thread: " + Thread.currentThread().getName());}}
}
在这个例子中,我们使用Executors.newCachedThreadPool()
方法创建了一个可缓存的线程池。然后,我们通过循环提交了10个任务到线程池中执行。每个任务都是一个实现了Runnable
接口的Task
类的实例。最后,我们调用executorService.shutdown()
方法关闭线程池。
ThreadLocal
Java中的ThreadLocal是一个用于存储线程局部变量的类,它允许每个线程拥有自己的独立变量副本,从而实现线程间的数据隔离。
ThreadLocal的主要作用是解决多线程环境下的数据安全问题,避免多个线程同时访问共享变量时出现数据不一致的情况。通过使用ThreadLocal,每个线程都可以在本地存储自己的私有数据,而不会影响其他线程的数据。
以下是ThreadLocal的一些主要特点:
- 线程安全:ThreadLocal为每个线程提供了独立的变量副本,避免了多线程之间的数据竞争和同步问题。
- 高效性:相比于使用同步机制来保护共享变量,ThreadLocal可以提供更高的性能,因为它避免了锁的使用和线程阻塞。
- 内存泄漏风险:由于ThreadLocal的生命周期与线程相同,如果不及时清理ThreadLocal中的数据,可能会导致内存泄漏。因此,在使用完ThreadLocal后,需要手动调用
remove()
方法来清除数据。 - 可扩展性:ThreadLocal可以通过继承或实现自定义的ThreadLocal子类来扩展其功能,以满足特定的业务需求。
- 适用场景:ThreadLocal适用于需要在多线程环境下保持线程间数据隔离的场景,例如数据库连接池、会话管理等。
下面是一个简单的示例代码,演示了如何使用ThreadLocal来存储和获取线程局部变量:
public class ThreadLocalExample {private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();public static void main(String[] args) {// 设置线程局部变量的值threadLocal.set("Hello, World!");// 获取线程局部变量的值String value = threadLocal.get();System.out.println(value); // 输出: Hello, World!// 清除线程局部变量的值threadLocal.remove();}
}
在这个例子中,我们创建了一个名为threadLocal
的ThreadLocal对象,并使用set()
方法设置了线程局部变量的值。然后,我们使用get()
方法获取了该值,并将其打印出来。最后,我们调用remove()
方法清除了线程局部变量的值,以避免内存泄漏。
原子性
Java中的原子性是指一个操作或者一系列操作要么全部执行成功,要么全部失败,且在执行过程中不会被其他线程打断。
原子性是并发编程中的一个重要概念,它保证了线程安全,即在一个线程执行操作时,不受其他线程的干扰。以下是保证原子性的几种方法:
- 使用synchronized关键字:通过同步代码块或同步方法来确保在同一时刻只有一个线程能够访问共享资源。
- 使用volatile关键字:虽然volatile不能保证复合操作的原子性,但它可以保证单个共享变量的读写操作是原子性的,并且能够保证变量的可见性。
- 使用Atomic类:Java提供了一系列的Atomic类(如AtomicInteger、AtomicLong等),它们使用非阻塞算法来实现对单个变量的原子操作。
- 使用Lock接口及其实现类:如ReentrantLock,它们提供了比synchronized更灵活的锁定机制,可以控制锁的获取和释放。
- 使用CAS操作:比较并交换(Compare-and-Swap)是一种无锁技术,用于实现高效的并发控制。
- 使用线程安全的数据结构:如ConcurrentHashMap、CopyOnWriteArrayList等,这些数据结构内部实现了必要的同步措施,以保证并发访问时的线程安全。
- 使用信号量、倒计时门闩等同步辅助工具:这些工具可以帮助控制并发线程的执行顺序和数量,从而保证操作的原子性。
以下是Java中保证原子性的代码示例:
- 使用synchronized关键字保证原子性:
public class AtomicityExample {private int count = 0;public synchronized void increment() {count++;}public synchronized int getCount() {return count;}
}
- 使用volatile关键字保证单个共享变量的原子性:
public class AtomicityExample {private volatile int count = 0;public void increment() {count++;}public int getCount() {return count;}
}
- 使用AtomicInteger保证单个共享变量的原子性:
import java.util.concurrent.atomic.AtomicInteger;public class AtomicityExample {private AtomicInteger count = new AtomicInteger(0);public void increment() {count.incrementAndGet();}public int getCount() {return count.get();}
}
- 使用Lock接口及其实现类ReentrantLock保证原子性:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class AtomicityExample {private int count = 0;private Lock lock = new ReentrantLock();public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}public int getCount() {return count;}
}
- 使用CAS操作保证原子性:
import java.util.concurrent.atomic.AtomicInteger;public class AtomicityExample {private AtomicInteger count = new AtomicInteger(0);public void increment() {int oldValue, newValue;do {oldValue = count.get();newValue = oldValue + 1;} while (!count.compareAndSet(oldValue, newValue));}public int getCount() {return count.get();}
}
并发工具类-Hashtable
Hashtable是Java集合框架中的一部分,它实现了Map接口,并且支持同步。
Hashtable在Java中是一个较早出现的集合类,它提供了一种存储键值对的方式,这些键值对被称为条目,它们存储在一个哈希表中。以下是关于Hashtable的一些详细信息:
- 实现和接口:Hashtable是java.util包的一部分,并且它是Dictionary接口的具体实现。自Java 2以来,Hashtable还实现了Map接口,这意味着它可以被当作一个映射来使用。
- 线程安全:与HashMap不同,Hashtable是线程安全的,因为它支持同步。这允许多个线程同时访问Hashtable而不会发生并发问题。
- 存储方式:Hashtable通过计算对象的哈希码来确定其在内部数组中的存储位置。当两个对象具有相同的哈希码时,它们会被存储在同一个索引位置,形成一个链表。
- 基本操作:Hashtable提供了常见的操作方法,如put()用于添加或更新键值对,get()用于根据键获取值,remove()用于删除键值对等。
- 性能考虑:由于Hashtable的同步特性,它在单线程环境下的性能可能不如非同步的HashMap。在高并发场景下,如果对性能有较高要求,可以考虑使用ConcurrentHashMap作为替代方案。
- 使用示例:Hashtable的使用非常简单,可以像下面这样创建一个Hashtable实例,并向其中添加元素:
Hashtable<Integer, String> hashtable = new Hashtable<>();
hashtable.put(1, "aa");
hashtable.put(4, "dd");
hashtable.put(2, "bb");
hashtable.put(3, "cc");
System.out.println(hashtable);
需要注意的是,虽然Hashtable是线程安全的,但在迭代时仍需手动同步以保证一致性。此外,Hashtable不允许键或值为null,这一点在使用时应特别注意。
并发工具类-CountDownLatch
CountDownLatch是Java并发编程中的一个同步辅助类,它允许一个或多个线程等待直到其他线程完成操作。
以下是CountDownLatch的一些主要特点和使用场景:
基本概念:CountDownLatch通过一个计数器来实现线程之间的同步。计数器的初始值由用户设置,每次调用countDown()
方法会使计数器的值减一,当计数器的值减至零时,所有因调用await()
方法而在等待的线程被唤醒。
应用场景:CountDownLatch通常用于以下场景:
- 等待其他线程完成任务后再执行:例如,主线程需要等待其他线程完成初始化操作后才能继续执行。
- 实现多个线程之间的同步:例如,确保所有线程都准备好后再同时开始执行。
使用示例:
import java.util.concurrent.CountDownLatch;public class CountDownLatchExample {public static void main(String[] args) throws InterruptedException {int threads = 5;CountDownLatch latch = new CountDownLatch(threads);for (int i = 0; i < threads; i++) {new Thread(new Worker(latch)).start();}latch.await(); // 主线程等待其他线程完成任务System.out.println("所有线程任务完成,主线程继续执行...");}
}class Worker implements Runnable {private final CountDownLatch latch;Worker(CountDownLatch latch) {this.latch = latch;}@Overridepublic void run() {try {// 模拟耗时操作Thread.sleep((long) (Math.random() * 1000));System.out.println(Thread.currentThread().getName() + " 任务完成");} catch (InterruptedException e) {e.printStackTrace();} finally {latch.countDown(); // 完成任务后,计数器减一}}
}
注意事项:
- CountDownLatch不能重置计数器,一旦计数器减至零,就不能再被使用。如果需要重复使用,需要创建新的CountDownLatch实例。
- 在调用
countDown()
和await()
方法时可能会抛出InterruptedException
,因此需要进行适当的异常处理
并发工具类-Semaphore
Java Semaphore是Java并发编程中的一个同步辅助类,它允许多个线程访问同一资源,但限制同时访问的线程数量。
以下是Semaphore的一些主要特点和使用场景:
基本概念:Semaphore通过一个计数器来实现线程之间的同步。计数器的初始值由用户设置,每次调用acquire()
方法会使计数器的值减一,当计数器的值减至零时,其他尝试获取许可的线程将被阻塞。当线程释放许可时,调用release()
方法会使计数器的值加一。
应用场景:Semaphore通常用于以下场景:
- 限制同时访问资源的线程数量:例如,限制同时访问数据库连接的线程数量,以避免过多的连接导致系统负载过高。
- 实现信号量机制:例如,控制生产者和消费者之间的生产消费速度,确保生产者不会过度生产而使缓冲区溢出。
使用示例:
import java.util.concurrent.Semaphore;public class SemaphoreExample {public static void main(String[] args) {int permits = 5; // 允许同时访问资源的线程数量Semaphore semaphore = new Semaphore(permits);for (int i = 0; i < 10; i++) {new Thread(new Worker(semaphore)).start();}}
}class Worker implements Runnable {private final Semaphore semaphore;Worker(Semaphore semaphore) {this.semaphore = semaphore;}@Overridepublic void run() {try {semaphore.acquire(); // 获取许可// 执行任务System.out.println(Thread.currentThread().getName() + " 正在执行任务");Thread.sleep((long) (Math.random() * 1000));} catch (InterruptedException e) {e.printStackTrace();} finally {semaphore.release(); // 释放许可}}
}
注意事项:
- Semaphore不能重置计数器,一旦计数器减至零,就不能再被使用。如果需要重复使用,需要创建新的Semaphore实例。
- 在调用
acquire()
和release()
方法时可能会抛出InterruptedException
,因此需要进行适当的异常处理。
相关文章:

Java 多线程补充
线程池 Java线程池是一种能够有效管理线程资源的机制,它可以显著提高应用性能并降低资源消耗。 线程池的主要优点包括: 资源利用高效:通过重用已存在的线程,减少了频繁创建和销毁线程带来的系统开销。响应速度提升:…...

【Java基础】Maven继承
1. 前言 Maven 在设计时,借鉴了 Java 面向对象中的继承思想,提出了 POM 继承思想。 2. Maven继承 当一个项目包含多个模块时,可以在该项目中再创建一个父模块,并在其 POM 中声明依赖,其他模块的 POM 可通过继承父模…...

java技术总结
1.java基本数据类型? byte 1,short 2 ,int 4,long 8 ,float 4,double 8,boolean 1,char 2 2.java为什么要有包装类型? 前 6 个类派生于公共的超类 Number,而 Character 和 Boolean 是 Object 的直接子类。 被 final 修饰, Java 内置的包装类是无法被继承的。 包装…...

C# WinForm —— 12 ListBox绑定数据
ListBox加载大量数据时,避免窗体闪烁的方法: 在加载语句的前后分别加上 BeginUpdate()方法 和 EndUpdate()方法 指定一个集合为绑定的数据源 1. 首先,右键项目,添加类 2. 在新建的类文件中添加属性值信息 3. 构建初始化的对象…...

自动驾驶主流芯片及平台架构(二)特斯拉自动驾驶芯片平台介绍
早期 对外采购mobileye EyeQ3 芯片摄像头半集成方案,主要是为了满足快速量产需求,且受制于研发资金不足限制; 中期 采用高算力NVIDIA 芯片平台其他摄像头供应商的特斯拉内部集成方案,mobileye开发节奏无法紧跟特斯拉需求ÿ…...

powershell@管道符过滤的顺序问题@powershell管道符如何工作
文章目录 select 和 where谁先执行powershell管道符stop-service 为例查看文档中的典型参数介绍stop-process为例介绍管道符传参是怎么工作的Id参数InputObject 参数Name参数额外的试验反面例子应用:get-process 和stop-process配合 select 和 where谁先执行 在执行筛选时&…...

SMI接口
目录 SMI 接口帧格式读时序写时序 IP 设计IP 例化界面IP 接口IP 验证 SMI 接口 SMI(Serial Management Interface)串行管理接口,也被称作 MII 管理接口(MII Management Interface),包括 MDC 和 MDIO 两条信…...

【C++】转换构造函数和类型转换函数
目录 转换构造函数转换构造函数调用 类型转换函数类型转换函数定义形式应用 转换构造函数 转换构造函数就是一种构造函数,将一个其他类型的数据转换成一个类的对象的构造函数。 类型->类对象 转换构造函数调用 (1)显式强制类型转换&…...

全栈开发之路——前端篇(5)组件间通讯和接口等知识补充
全栈开发一条龙——前端篇 第一篇:框架确定、ide设置与项目创建 第二篇:介绍项目文件意义、组件结构与导入以及setup的引入。 第三篇:setup语法,设置响应式数据。 第四篇:数据绑定、计算属性和watch监视 辅助文档&…...

4.【Orangepi Zero2】Linux定时器(signal、setitimer),软件PWM驱动舵机(SG90)
Linux定时器(signal、setitimer),软件PWM驱动舵机(SG90) signalsetitimer示例 软件PWM驱动舵机(SG90) signal 详情请看Linux 3.进程间通信(shmget shmat shmdt shmctl 共享内存、si…...

K8S哲学 - 资源调度 HPA (horizontal pod autoScaler-sync-period)
kubectl exec: kubectl exec -it pod-name -c container-name -- /bin/sh kubectl run 通过一个 deployment来 演示 apiVersion: apps/v1 kind: Deployment metadata:name: deploylabels: app: deploy spec: replicas: 1selector: matchLabels:app: deploy-podt…...

uniapp/微信小程序实现加入购物车点击添加飞到购物车动画
1、预期效果 2、实现思路 每次点击添加按钮时,往该按钮上方添加一个悬浮元素,通过位移动画将元素移到目标位置。 1. 为每个点击元素设置不同的class,才能通过uni.createSelectorQuery来获取每个元素的节点信息; 2. 添加一个与…...

电商大数据的采集||电商大数据关键技术【基于Python】
.电商大数据采集API 什么是大数据? 1.大数据的概念 大数据即字面意思,大量数据。那么这个数据量大到多少才算大数据喃?通常,当数据量达到TB乃至PB级别时,传统的关系型数据库在处理能力、存储效率或查询性能上可能会遇…...

H264 SP帧等知识笔记
H.264是一种广泛使用的视频编码标准,它使用多种类型的帧来实现高效的视频压缩。在H.264中,参考帧和重建帧是两个重要的概念,它们之间既有区别又有联系。 参考帧: 参考帧是用于预测其他帧的帧。在H.264中,编码器会利用…...

流量印钞机:每日稳定收入1500+
标题:“流量印钞机:每日稳定收入1500” 随着互联网的迅速发展,越来越多的人开始利用网络平台来赚取稳定的收入。在这个信息爆炸的时代,拥有了一定的流量就意味着拥有了一台“印钞机”,可以每日稳定地创造超过1500元的…...

Tomcat中服务启动失败,如何查看启动失败日志?
1. 查看 localhost.log 这个日志文件通常包含有关特定 web 应用的详细错误信息。运行以下命令查看 localhost.log 中的错误: sudo tail -n 100 /opt/tomcat/latest/logs/localhost.YYYY-MM-DD.log请替换 YYYY-MM-DD 为当前日期,或选择最近的日志文件日…...

React19学习-初体验
升级react19版本 安装 npm install reactbeta react-dombeta如果使用ts则需要在package.json中添加。等正式版发布直接可以使用types/react了 "overrides": {"types/react": "npm:types-reactbeta","types/react-dom": "npm:ty…...

【UE5】数字人基础
这里主要记录一下自己在实现数字人得过程中涉及导XSens惯性动捕,视频动捕,LiveLinkFace表捕,GRoom物理头发等。 一、导入骨骼网格体 骨骼网格体即模型要在模型雕刻阶段就要雕刻好表捕所需的表情体(blendshape),后面表捕的效果直…...

OSTEP Projects:KV
本文将介绍操作系统导论(Operating Systems: Three Easy Pieces)作者所开源的操作系统相关课程项目 的 KV 部分,包含个人的代码实现和设计思路。 思路 题目要求实现一个最简单的数据库,以支持数据的持久化。 每个操作由格式为 o…...

JAVA学习笔记(第三周)
文章目录 继承概述使用场景继承的特点子类继承的内容成员变量访问特点成员方法访问特点方法的重写构造方法this super 多态多态的表现形式多态的前提成员变量和方法调用instanceof优势弊端 包包名的规则全类名final常量 权限修饰符代码块 继承 概述 继承就是子类继承父类的特征…...

linux 内核驱动 -- reboot -f 导致内核死机 而 reboot则不会引起问题
问题描述,定于与解决:...

【vue-echarts】 报错问题解决 “Error: Component series.pie not exists. Load it first.“
目录 问题描述解决【解决1】【解决2】 问题描述 使用 vue-echarts 时导入的文件 import VChart from vue-echarts/components/ECharts import echarts/lib/chart/line import echarts/lib/chart/bar import echarts/lib/chart/pie import echarts/lib/component/legend impor…...

MySQL慢查询SQL优化
一、慢查询日志 描述:通过慢查询日志等定位那些执行效率较低的SQL语句 查看 # 慢查询是否开启 show variables like slow_query_log%; # 慢查询超时时间 show variables like long_query_time%;执行SQL 开启慢查询日志 set global slow_query_log ON;设置慢查…...

【嵌入式DIY实例】-DDS信号生成器
DDS信号生成器 文章目录 DDS信号生成器1、AD9805介绍2、硬件准备与接线3、代码实现在本文中,将详细介绍如何使用AD9850来搭建一个简易的DDS(Direct Digital signal )信号生成器。 1、AD9805介绍 AD9850是一款高度集成的器件,采用先进的DDS技术,内置一个高速、高性能数模转…...

java设计模式四 桥接模式
桥接模式关注于将抽象部分与实现部分分离,使它们可以独立变化。它通过在抽象和实现之间建立一个桥梁来实现这一目的。这种设计模式属于结构型模式。 假设我们要设计一个图形编辑器,其中图形(如圆形、正方形)可以有不同的颜色填充…...

《Python编程从入门到实践》day24
# 昨日知识点学习 创建外星人从一个到一行 # 主程序snipdef _create_fleet(self):"""创建外星人群"""# 创建一个外星人并计算一行可容纳多少个外星人# 外星人的间距为外星人的宽度alien Alien(self)alien_width alien.rect.widthavailable_sp…...

【hackmyvm】 Animetronic靶机
靶机测试 arp-scanporturl枚举exiftool套中套passwordsudo 提权 arp-scan arp-scan 检测局域网中活动的主机 192.168.9.203 靶机IP地址port 通过nmap扫描,获取目标主机的端口信息 ┌──(root㉿kali)-[/usr/share/seclists] └─# nmap -sT -sV -O 192.16…...

[附源码]石器时代_恐龙宝贝内购版_三网H5手游_带GM工具
石器时代之恐龙宝贝内购版_三网H5经典怀旧Q萌全网通手游_Linux服务端源码_视频架设教程_GM多功能授权后台_CDK授权后台 本教程仅限学习使用,禁止商用,一切后果与本人无关,此声明具有法律效应!!!࿰…...

RS2255XN功能和参数介绍及PDF资料
RS2255XN是一款由Runic(润石)公司生产的模拟开关。以下是关于RS2255XN的一些技术参数和特点: 封装:MSOP-10 电源电压范围:2.5V至5.5V 工作温度范围:-40C至125C 类型:模拟开关 品牌:R…...

设计模式——外观模式(Facade)
外观模式(Facade Pattern) 是一种结构型设计模式,它为一个子系统中的一组接口提供一个统一的高层接口,使得子系统更加容易使用。这种类型的设计模式属于结构型模式,它向客户端提供了一个接口,隐藏了子系统的…...