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 ...
随机推荐
- Winform 点击TreeView控件节点的CheckBox不触发NodeMouseClick事件的做法
private void Tv_areainfo_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) { if (!e.Node. ...
- GIS基础软件及操作(十一)
原文 GIS基础软件及操作(十一) 练习十一.3D 可视分析 1.对地理数据进行透视观察.三维浏览:2.制作飞行动画 GIS数据三维显示 (1) 运行程序:[开始菜单]>>[所有程序]&g ...
- Markdown 入门
一. Markdown语法的简要规则 标题 标题是非常重要的一个标记,一段文字标记为标题,只需要在文字前加 #.具体可以支持到1到6个# 1 2 3 4 # 一级标题 ## 二级标题 ### 三级标题 ...
- 有未经处理的异常(在 xx.exe 中): 堆栈 Cookie 检测代码检测到基于堆栈的缓冲区溢出。
一般这个问题是数组越界. 我产生这个异常的代码是这句:memcpy(tmp_cert.byKey, m_row[2], 255); 255的长度超过了char数组tmp_cert.byKey的长度.
- 01 Python初探
Python初探 Become a better version of yourself! 诞生 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年开始写Python ...
- Android CTS Test failed to run to conmpletion 测试超时问题
引用“Android cts all pass 全攻略”里面的一段话: ❀ testcase timeout 测试某个testcase的时候一直出现 “........”,迟迟没有pass或者fail ...
- 宿主机与虚拟机系统的USB设备切换
有时候我们需要在虚拟机的操作系统中进行一些USB设备的测试,但默认情况下USB设备是在宿主机系统里面的,那这个时候我们就要进行切换才能够达到目的,具体要怎么操作呢?下面讲解一下: 1. Ctrl+ ...
- 梭子鱼VS多备份 虽殊途却同归
备份,对于企业来说,不仅是一个已经拥有多年历史的传统IT工作,还关系着企业自身的生死存亡.在云计算时代下,备份业务成为企业的必选项,已经成为云计算服务最为热门的领域之一.基于云的备份正在深刻改变着备份 ...
- linux如何直接运行python文件
1.首先创建好py文件 touch GetCiscoCpu.py 2. 编写GetCiscoCpu.py vi GetCiscoCpu.py /********************** *本文是用 ...
- Java基础(六) static五大应用场景
static和final是两个我们必须掌握的关键字.不同于其他关键字,他们都有多种用法,而且在一定环境下使用,可以提高程序的运行性能,优化程序的结构.上一个章节我们讲了final关键字的原理及用法,本 ...