Java 多线程补充

线程池

Java线程池是一种能够有效管理线程资源的机制,它可以显著提高应用性能并降低资源消耗

线程池的主要优点包括:

  1. 资源利用高效:通过重用已存在的线程,减少了频繁创建和销毁线程带来的系统开销。
  2. 响应速度提升:任务到来时可以迅速被执行,而不必等待新线程的创建。
  3. 管理监控便捷:线程数量有限,避免无限制创建线程导致的资源耗尽和系统不稳定问题,同时便于统一分配、调优和监控。
  4. 功能丰富强大:提供了多种类型的线程池,如定时、定期以及可控线程数的线程池,满足不同的业务需求。

在使用线程池时,需要注意以下几点:

  1. 合理配置参数:创建线程池时需要根据实际需求合理设置线程数量等参数,以避免资源浪费或系统过载。
  2. 预防潜在风险:使用Executors类中的便捷方法虽然简单,但可能会隐藏复杂性,如不当使用可能导致内存溢出(OOM)或线程耗尽等问题。
  3. 选择正确任务类型:理解RunnableCallable接口的区别,并根据任务的特性选择合适的类型提交给线程池执行。

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的一些主要特点:

  1. 线程安全:ThreadLocal为每个线程提供了独立的变量副本,避免了多线程之间的数据竞争和同步问题。
  2. 高效性:相比于使用同步机制来保护共享变量,ThreadLocal可以提供更高的性能,因为它避免了锁的使用和线程阻塞。
  3. 内存泄漏风险:由于ThreadLocal的生命周期与线程相同,如果不及时清理ThreadLocal中的数据,可能会导致内存泄漏。因此,在使用完ThreadLocal后,需要手动调用remove()方法来清除数据。
  4. 可扩展性:ThreadLocal可以通过继承或实现自定义的ThreadLocal子类来扩展其功能,以满足特定的业务需求。
  5. 适用场景: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中的原子性是指一个操作或者一系列操作要么全部执行成功,要么全部失败,且在执行过程中不会被其他线程打断

原子性是并发编程中的一个重要概念,它保证了线程安全,即在一个线程执行操作时,不受其他线程的干扰。以下是保证原子性的几种方法:

  1. 使用synchronized关键字:通过同步代码块或同步方法来确保在同一时刻只有一个线程能够访问共享资源。
  2. 使用volatile关键字:虽然volatile不能保证复合操作的原子性,但它可以保证单个共享变量的读写操作是原子性的,并且能够保证变量的可见性。
  3. 使用Atomic类:Java提供了一系列的Atomic类(如AtomicInteger、AtomicLong等),它们使用非阻塞算法来实现对单个变量的原子操作。
  4. 使用Lock接口及其实现类:如ReentrantLock,它们提供了比synchronized更灵活的锁定机制,可以控制锁的获取和释放。
  5. 使用CAS操作:比较并交换(Compare-and-Swap)是一种无锁技术,用于实现高效的并发控制。
  6. 使用线程安全的数据结构:如ConcurrentHashMap、CopyOnWriteArrayList等,这些数据结构内部实现了必要的同步措施,以保证并发访问时的线程安全。
  7. 使用信号量、倒计时门闩等同步辅助工具:这些工具可以帮助控制并发线程的执行顺序和数量,从而保证操作的原子性。

以下是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,因此需要进行适当的异常处理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/835198.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

智慧公厕,小民生里的“大智慧”!

公共厕所是城市社会生活的基础设施&#xff0c;而智慧公厕则以其独特的管理模式为城市居民提供更优质的服务。通过智能化的监测和控制系统&#xff0c;智慧公厕实现了厕位智能引导、环境监测、资源消耗监测、安全防范管理、卫生消杀设备、多媒体信息交互、自动化控制、自动化清…

ThinkPHP+MySQL查询数据的时候计算两个经纬度之间的距离

需求&#xff0c;数据表中有lng&#xff08;经度&#xff09;lat&#xff08;维度&#xff09;两个字段&#xff0c;查询数据的时候要计算记录经纬度距离目标经纬度之间的距离。 方法中还有根据生日计算年龄(YEAR(CURDATE()) - YEAR(birthday)) AS age public function get_li…

抽象类基本概念

抽象类及抽象方法 概念&#xff1a;一个类中没有包含足够的信息来描绘一个具体的对象&#xff0c;这种类被定义为抽象类&#xff0c;含有抽象方法的类也被称为抽象类。 用通俗的话来说就是当一个类的某个功能&#xff08;方法&#xff09;实现不确定时&#xff0c;我们就将该…

Java防挨骂--01

在遇到字符拼接需求时&#xff0c;习惯使用StringBuilder,而不要使用String. 因为String是不可变字符序列&#xff0c;在拼接时会产生新的String对象来进行拼接 StringBuider是可变字符序列&#xff0c;在拼接时效率和对资源的占用都表现更优。 StringBuffer也是可变字符序列…

一篇详解Git版本控制工具

华子目录 版本控制集中化版本控制分布式版本控制 Git简史Git工作机制Git和代码托管中心局域网互联网 Git安装基础配置git的--local&#xff0c;--global&#xff0c;--system的区别 创建仓库方式1git init方式2git clone git网址 工作区&#xff0c;暂存区&#xff0c;本地仓库…

React19学习-初体验

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

ethtool 调用流程

1. bcmgenet_ethtool_ops 分析 static const struct ethtool_ops bcmgenet_ethtool_ops {.begin bcmgenet_begin,.complete bcmgenet_complete,.get_strings bcmgenet_get_strings,.get_sset_count bcmgenet_get_sset_count,.get_ethtoo…

Spring添加注解读取和存储对象

5大注解 Controller 控制器 Service 服务 Repository 仓库 Componet 组件 Configuration 配置 五大类注解的使用 //他们都是放在同一个目录下&#xff0c;不同的类中 只不过这里粘贴到一起//控制器 Controller public class UserController {public void SayHello(){System.ou…

在51单片机里面学习C语言

在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「&#xff23;语言的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01; 说出来你们可能都…

Oracle count的优化-避免全表扫描

Oracle count的优化-避免全表扫描 select count(*) from t1; 这句话比较简单&#xff0c;但很有玄机&#xff01;对这句话运行的理解&#xff0c;反映了你对数据库的理解深度&#xff01; 建立实验的大表他t1 SQL> conn scott/tiger 已连接。 SQL> drop table t1 purge…

C++ | Leetcode C++题解之第71题简化路径

题目&#xff1a; 题解&#xff1a; class Solution { public:string simplifyPath(string path) {auto split [](const string& s, char delim) -> vector<string> {vector<string> ans;string cur;for (char ch: s) {if (ch delim) {ans.push_back(mov…

QT+MYSQL数据库处理

1、打印Qt支持的数据库驱动&#xff0c;看是否有MYSQL数据库驱动 qDebug() << QSqlDatabase::drivers(); 有打印结果可知&#xff0c;没有MYSQL数据库的驱动 2、下载MYSQL数据库驱动&#xff0c;查看下面的文章配置&#xff0c;亲测&#xff0c;可以成功 Qt6 配置MySQL…

百面算法工程师 | 模型评价指标及优化策略

本文给大家带来的百面算法工程师是深度学习模型评价指标的面试总结&#xff0c;文章内总结了常见的提问问题&#xff0c;旨在为广大学子模拟出更贴合实际的面试问答场景。在这篇文章中&#xff0c;我们还将介绍一些常见的评价方案&#xff0c;并提供参考的回答及其理论基础&…

7 系列 FPGA 产品介绍及选型

目录 Spartan-7 FPGAsArtix-7 FPGAsKintex-7 FPGAsVirtex-7 FPGAsFPGA芯片命名规则DSP资源BRAM资源Transceivers 资源Transceivers 总带宽I/O 个数及带宽参考文档 Spartan-7 FPGAs Artix-7 FPGAs Kintex-7 FPGAs Virtex-7 FPGAs FPGA芯片命名规则 DSP资源 BRAM资源 Transceiver…

深入浅出JavaScript继承机制:解密原型、原型链与面向对象实战攻略

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 &#x1f525; 引言&#x1f9f1; 原型基础⛓️ 原型链的形成&#x1f504; 修改原型的影响&#x1f3c1; 原型链的尽头为什么null标志着结束&#xff1f;实际意义 &#x1f310; &#x1f504; 继承的实现方式1. 原型链继承…

数据库期末复习资料

考纲 选择 10分 简答题 25分 ER图建表 15分 建表以后的范式的分解、规范化 15分 Sql语句 35分 &#xff1a;关系代数&#xff0c;select&#xff0c;触发器 --------------------------------------数据库设计----------------------------------- 第一章&#xff1a; 1.数据…

我在洛杉矶采访到了亚马逊云全球首席信息官CISO(L11)!

在本次洛杉矶举办的亚马逊云Re:Inforce全球安全大会中&#xff0c;小李哥作为亚马逊大中华区开发者社区和自媒体代表&#xff0c;跟着亚马逊云安全产品团队采访了亚马逊云首席信息安全官(CISO)CJ Moses、亚马逊副总裁Eric Brandwine和亚马逊云首席高级安全工程师Becky Weiss。 …

安装docker镜像nginx1.26.0版本,与删除docker容器【灵异事件】

为了http3 的这个模块&#xff0c;所以需要升级nginx的版本&#xff0c;需要nginx1.26.0才有 –with-http_v3_module 这个模块 为什么记录一下&#xff1f;因为觉得奇怪 1&#xff1a;删除nginx镜像&#xff0c;显示镜像还被某个容器在使用 luichunluichun:~$ docker rmi ng…

Gone框架介绍13 - Gone的Hook函数

我从头到尾实现了一个Golang的依赖注入框架&#xff0c;并且集成了gin、xorm、redis、cron、消息中间件等功能&#xff1b;自己觉得还挺好用的&#xff0c;推荐给你&#xff01;也欢迎一起维护&#xff01; github地址&#xff1a;https://github.com/gone-io/gone 文档地址&am…

文心一言 VS 讯飞星火 VS chatgpt (255)-- 算法导论18.3 1题

一、请说明依次从图18-8(f)中删除C、P和V后的结果。如果要写代码&#xff0c;请用go语言。 文心一言&#xff1a; 首先&#xff0c;让我们明确一点&#xff1a;由于您没有提供具体的图18-8(f)的内容&#xff0c;我只能假设它是一个数据结构&#xff08;可能是图、树或列表等&…