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 ... 
随机推荐
- Delphi中返回类型为string的函数的一个陷阱(不是很懂)
			如果类的一个成员函数的返回值是string类型,需要注意一个问题 其返回值可能是错误的 例如函数的实现如下 function GetString( s: string ): string;begin ... 
- 关于powerdesigner中的data types说明
			原文:关于powerdesigner中的data types说明 这一堆的数据类型看着真是头大,弄个表格对照一下. Numeric data types Standard datatype DBMS- ... 
- enum 枚举一般用法 dotnet
			public enum Demo { [Description("Moning描述")] Moning = , [Description("Afternoon描述&quo ... 
- QT 那些事
			一直做的是windows开发,有2个小项目想做到手机.看来看去,选择了QT.本文介绍自己的学习的一些心得,希望对你有帮助.1.选择QT2.安卓的一些事3.QML调用C++4.资源集成(比较有用,网上资 ... 
- JavaScript 数据实用程序库:Datalib
			Datalib 是一个 JavaScript 数据实用程序库. 快速使用Romanysoft LAB的技术实现 HTML 开发Mac OS App,并销售到苹果应用商店中. <HTML开发M ... 
- 由Qmake.exe/QtCreator.exe启动速度慢挖进去(非常有趣的调试过程,作者态度不错,而且关闭Welcome插件也是常见办法)
			一直用Qt Creator开发Qt程序,Nokia的Qt Creator实在太慢了,启动慢,编译速度也是超级慢.昨天,终于它慢的让我无法忍受了,我决定抛开手上的一切工作,深入挖掘Qt Creator启 ... 
- 使用AnimateWindow来实现窗口淡入淡出(主要有四种动画,滚动,滑动,折叠或展开,和淡入淡出)
			如果是在VC6下进行编译,应引入下面的预编译宏,注意放在windows.h的前面#undef WINVER #define WINVER 0x500为什么要引入上面的宏呢?看看winuse ... 
- 为QNetworkAccessManager添加超时提醒(自己记录一段时间里的下载字节数,用定时器去定期检测,从而判断是否超时)
			在做更新,在测试异常的时候,在下载过程中,发现如果直接系统禁用了网络,会报错误,可以捕获.但是如果是第三方软件限制程序联网,问题来了. 程序会一直在那里等待,没有异常,也不发送QNetworkAcce ... 
- asp.net mvc实现微信外H5支付方法
			一.微信支付方式介绍 微信提供了各种支付方式,试用于各种不同的支付场景,主要有如下几种: 1.刷卡支付 刷卡支付是用户展示微信钱包内的“刷卡条码/二维码”给商户系统扫描后直接完成支付的模式.主要应用线 ... 
- 【工具】Axure 8.0 序列号
			之前用的 Axure 8.0 到期最近了,重找了一个序列号,发现可用,记录一下,分享如下: 授权人:University of Science and Technology of China (CLA ... 
