Java并发之线程封闭
读者们好! 在这篇博客中,我们将探讨线程封闭是什么意思,以及我们如何实现它。 所以,让我们直接开始吧。
1. 线程封闭
大多数的并发问题仅发生在我们想要在线程之间共享可变变量或可变状态时。如果在多个线程之间操作共享变量,则所有线程都将能够读取和修改变量的值,从而出现意外或不正确的结果。一种简单的避免此问题的方式是不在线程之间共享数据。 这种技术称为线程封闭,是在我们的应用程序中实现线程安全的最简单方法之一。
Java 语言本身没有任何强制执行线程封闭的方法。线程封闭是通过不允许多个线程同时使用同一个状态的方式的程序设计来实现的,因此由程序实现强制执行。 几种类型的线程封闭,如下所示:
1.1 Ad-Hoc 线程封闭
Ad-hoc 线程封闭描述了线程封闭的方式,由开发人员或从事该项目的开发人员确保仅在单个线程内使用此对象。 这种方式方法可用性不高,在大多数情况下应该避免。
Ad-hoc 线程封闭下的一个特例适用于 volatile 变量。 只要确保 volatile 变量仅从单个线程写入,就可以安全地对共享 volatile 变量执读 - 改 - 写操作。在这种情况下,您将修改限制在单个线程以防止竞争条件,并且 volatile 变量的可见性保证确保其他线程看到最新值。
1.2 栈封闭
栈封闭将变量或对象封闭在线程的栈中。这比 Ad-hoc 线程封闭强得多,因为它通过定义堆栈本身中的变量状态来进一步限制对象的范围。例如,请考虑以下代码:
private long numberOfPeopleNamedJohn(List<Person> people) {
List<Person> localPeople = new ArrayList<>();
localPeople.addAll(people);
return localPeople.stream().filter(person -> person.getFirstName().equals("John")).count();
}
在上面的代码中,我们传递一个 person 对象的 list 但不直接使用它。 相反,我们创建自己的 list,该 list 是当前正在执行的线程的本地 list,并将变量 people中的所有 person 添加到 localPeople。由于我们仅在 numberOfPeopleNamedJohn方法中定义列表,这使得变量localPeople 受到堆栈隔离保护,因为它只存在于一个线程的堆栈上,因此任何其他线程都无法访问它。这使得 localPeople 线程安全。 唯一需要注意的是,不应该让 localPeople 的作用于超过这个方法的范围,以保证堆栈的隔离控制。在定义这个变量时,应该记录或注释为什么要定义这个变量,通常,只有在当前开发人员的脑海中才不让它超出方法的作用域,但是在将来,另一个开发人员可能会不知道为何如此设计而陷入困境。
1.3 ThreadLocal
ThreadLocal允许我们将每个线程 ID 与相应对象的值相关联。 它允许我们为不同的线程存储不同的对象,并维护哪个对象对应于哪个线程。它有 set 和 get 方法,这些方法为使用它的每个线程维护一个单独的 value 副本。get() 方法总是返回从当前正在执行的线程传递给 set()的最新值。 我们来看一个例子:
public class ThreadConfinementUsingThreadLocal {
public static void main(String[] args) {
ThreadLocal<String> stringHolder = new ThreadLocal<>();
Runnable runnable1 = () -> {
stringHolder.set("Thread in runnable1");
try {
Thread.sleep(5000);
System.out.println(stringHolder.get());
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Runnable runnable2 = () -> {
stringHolder.set("Thread in runnable2");
try {
Thread.sleep(2000);
stringHolder.set("string in runnable2 changed");
Thread.sleep(2000);
System.out.println(stringHolder.get());
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Runnable runnable3 = () -> {
stringHolder.set("Thread in runnable3");
try {
Thread.sleep(5000);
System.out.println(stringHolder.get());
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread thread1 = new Thread(runnable1);
Thread thread2 = new Thread(runnable2);
Thread thread3 = new Thread(runnable3);
thread1.start();
thread2.start();
thread3.start();
}
}
在上面的例子中,我们使用相同的 ThreadLocal 对象 stringHolder 执行了三个线程。正如你在这里看到的,我们首先在 stringHolder 对象的每个线程中设置一个字符串,使其包含三个字符串。然后,经过一些暂停后,我们只更改了第二个线程的值。 以下是该程序的输出:
string in runnable2 changed
Thread in runnable1
Thread in runnable3
正如您在上面的输出中所看到的,线程2的字符串已更改,但线程1和线程3的字符串未受影响。如果我们在从 ThreadLocal 获取特定线程的值之前没有设置任何值,那么它返回null。 线程终止后,“ThreadLocal” 中特定于线程的对象就可以进行垃圾回收了。
这就是线程封闭。 我希望这个博客对你有所帮助,一块学习新的东西。 谢谢!
原文:https://dzone.com/articles/java-concurrency-thread-confinement
Java并发之线程封闭的更多相关文章
- 并发之线程封闭与ThreadLocal解析
并发之线程封闭与ThreadLocal解析 什么是线程封闭 实现一个好的并发并非易事,最好的并发代码就是尽量避免并发.而避免并发的最好办法就是线程封闭,那什么是线程封闭呢? 线程封闭(thread c ...
- Java并发编程--线程封闭(Ad-hoc封闭 栈封闭 ThreadLocal)
线程封闭实现好的并发是一件困难的事情,所以很多时候我们都想躲避并发.避免并发最简单的方法就是线程封闭.什么是线程封闭呢?就是把对象封装到一个线程里,只有这一个线程能看到此对象.那么这个对象就算不是线程 ...
- Java并发之线程中断
前面的几篇文章主要介绍了线程的一些最基本的概念,包括线程的间的冲突及其解决办法,以及线程间的协作机制.本篇主要来学习下Java中对线程中断机制的实现.在我们的程序中经常会有一些不达到目的不会退出的线程 ...
- Java并发之线程管理(线程基础知识)
因为书中涵盖的知识点比较全,所以就以书中的目录来学习和记录.当然,学习书中知识的时候自己的思考和实践是最重要的.说到线程,脑子里大概知道是个什么东西,但很多东西都还是懵懵懂懂,这是最可怕的.所以想着细 ...
- Java并发之线程转储
一.java线程转储 java的线程转储可以被定义为JVM中在某一个给定的时刻运行的所有线程的快照.一个线程转储可能包含一个单独的线程或者多个线程.在多线程环境中,比如J2EE应用服务器,将会有许多线 ...
- java并发之线程池的使用
背景 当系统并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要消耗大量的系统资源. 所以需要一个办法使得线程可以 ...
- Java并发之——线程池
一. 线程池介绍 1.1 简介 线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡 ...
- Java 并发之线程安全
写线程安全的代码,说白了就是管理一个类的共享的.可变的状态.只要有多于 1 个线程对类的状态进行写入,那么就必须用同步来协调这多个线程对状态的访问.对于一个没有状态的类来说(简单的理解就是只有方法没有 ...
- Java并发之线程异常捕获
由于线程的本质特性,使得你不能捕获从线程中逃逸的异常,如: import java.util.concurrent.ExecutorService; import java.util.concurre ...
随机推荐
- .NET中System.Diagnostics.Stopwatch、System.Timers.Timer、System.Threading.Timer 的区别
1.System.Diagnostics.Stopwatch Stopwatch 实例可以测量一个时间间隔的运行时间,也可以测量多个时间间隔的总运行时间. 在典型的 Stopwatch 方案中,先调用 ...
- 赵伟国:陆资无法进入台湾紫光要到WTO控告(芯片是为了经济安全,高通找的人不是很聪明)
集微网消息,昨天由全球半导体联盟和上海市集成电路行业协会联合举办的Memory +论坛在上海举行,会议透过来自存储器.逻辑和系统市场领先企业的高管,深入他们对未来存储器的应用.可行的商业模式,以及逻辑 ...
- SqlServer 使用脚本创建分发服务及事务复制的可更新订阅
原文:SqlServer 使用脚本创建分发服务及事务复制的可更新订阅 [创建使用本地分发服务器] /************************[使用本地分发服务器配置发布]*********** ...
- PHPEXCEL 不能输出中文内容,只显示空白
以他带的示例文件为例 01simple-download-xls.php // Add some data $objPHPExcel->setActiveSheetIndex(0) ...
- 毕设(二)C#SerialPort
毕业设计中,用到串口与无人机通信,所以就用到了SerialPort这个类,这个类在设置属性时, 用到最主要的属性应该是COM口和波特率,由于本人不熟悉硬件,不便多说,但经验告诉我是这样的, 还有数据位 ...
- Rainyday.js – Rendering Raindrops with JavaScript
Posted · Category:GPL License,Tools 直击现场 The idea behind Rainyday.js is to create a JavaScript libra ...
- 知识的内化:学习、实践、输出(与Focus Feedback FixIt的原理是一致的)
一个人的能力分三个层次: 资源,比如知识.技能.经验.时间.精力.金钱.人脉等 应用流程,即使用资源解决问题的能力,包括做事的方法.流程.策略等,它是你整合应用资源创造价值的能力. 价值取向,即你觉得 ...
- SQLite实现内存键值存储
SQLite数据文件往Linux内存文件系统/dev/shm/data.sqlite3一放,就是内存级读写性能的SQL系统.用SQLite实现内存键值存储:CREATE TABLE IF NOT EX ...
- URL重写 httpModules IIS7
<system.web> <httpModules> <!--URL重写:IIS 及以下用次处配置--> <!--add name="MyHttpM ...
- EF 6.0 Code First 迁移MySql数据库
一.准备工作 使用NUGET安装Entity Framework 6,下载MySql Connector/Net 6.9.5 二.创建实体 我们在下面创建了两个类(博客和文章),并 ...