Java - 线程封闭
保证并发安全性的方式有三:
不共享、不可变、同步
前两种方式相对第三种要简单一些。
这一篇不说语言特性和API提供的相关同步机制,主要记录一下关于共享的一些思考。
共享(shared),可以简单地认为多个线程可以同时访问某个对象。
如果仅仅在单线程内进行访问则不存在同步的问题。
保证数据的单线程访问称为线程封闭(thread confinement)。
线程封闭有三种方式:
·Ad-hoc线程封闭
·栈封闭
·ThreadLocal
Ad-hoc线程封闭:
通过程序实现来进行线程封闭,也就是说我们无法利用语言特性将对象封闭到特定的线程上,这一点导致这种方式显得不那么可靠。
举个例子,假设我们保证只有一个线程可以对某个共享的对象进行写入操作,那么这个对象的"读取-修改-写入"(比如自增操作)在任何情况下都不会出现竟态条件。
如果我们为这个对象加上volatile修饰则可以保证该对象的可见性,任何线程都可以读取该对象,但只有一个线程可以对其进行写入。
这样,仅仅通过线程封闭+volatile修饰就适当地保证了其安全性,相比直接使用synchoronized修饰,虽然更适合,但实现起来稍微复杂。
而对于线程封闭方式的选择,这种方式是最不被推荐的。
栈封闭:
这个方式理解起来比较简单,封闭在执行线程是局部变量本身固有的特性,封闭在执行线程的栈里,其他线程无法访问是理所当然的。
对于基本类型的局部变量,我们不用考虑任何事情,因为Java语言特性本身就保证了任何方法都无法获得基本类型的引用。
而对于引用类型的局部变量,我们需要稍微注意一些问题来保证其栈封闭。
参考下面的装载方舟的代码,现在我们要保护animals,则需要保证该方法的参数、调用的外来方法、返回值都不会引用到animals:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public int loadTheArk(Collection<Animal> candidates) { SortedSet<Animal> animals; int numPairs = 0; Animal candidate = null; animals = new TreeSet<Animal>(new SpeciesGenderComparator()); animals.addAll(candidates); for (Animal a : animals) { if (candidate == null || !candidate.isPotentialMate(a)) candidate = a; else { ark.load(new AnimalPair(candidate, a)); ++numPairs; candidate = null; } } return numPairs; } |
先说说loadTheArk的参数candidates,我们将它的元素进行筛选后装载到了方舟中,方法结束后无法通过该参数影响方舟中的动物夫妇。
其次是外来方法,我们使用了"种类性别比较器"对animals进行排序,但它是一个concrete,不会有不确定的行为对animals的状态产生影响。
最后是返回值,显然我们是想报告装载了多少对动物夫妇,返回类型是个基本类型,无法引用animals。
好了,这就是个成功的栈封闭。
ThreadLocal:
给人一种亲切感,这几乎是很常见的方式,而且也是最规范的方式。
我们通常用ThreadLocal保证可变的单例变量和全局变量不被多线程共享。
先让我们想想单线程场景中使用Connection对象连接数据库,鉴于Connection对象的初始化开销,整个应用中会维护一个全局的Connection对象。
如果我们想将这个应用改为多线程的,鉴于Connection对象本身不是线程安全的,我们需要对其进行线程封闭,此时我们可以使用ThreadLocal:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public class ConnectionDispenser { static String DB_URL = "jdbc:mysql://localhost/mydatabase"; private ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() { public Connection initialValue() { try { return DriverManager.getConnection(DB_URL); } catch (SQLException e) { throw new RuntimeException("Unable to acquire Connection, e"); } }; }; public Connection getConnection() { return connectionHolder.get(); }} |
不仅是Connection这种场景,如果我们的很多操作频繁地用到某个对象,而我们又需要考虑它的线程封闭又需要考虑它的初始化开销,ThreadLocal几乎是最好的选择。
虽然这看起来有点像一个全局的Map<Thread,T>,事实上也可以这样理解,但其实现并不是这样你懂的。
当然,这种方式很方便,但这并不代表ThreadLocal可以滥用, 比如仅仅是考虑到应用的并发安全性就把全局变量一律变成ThreadLocal。
而这种做法会导致全局变量难以抽象,并降低其可重用性,而且也增加了耦合。
Java - 线程封闭的更多相关文章
- Java中ThreadLocal无锁化线程封闭实现原理
虽然现在可以说很多程序员会用ThreadLocal,但是我相信大多数程序员还不知道ThreadLocal,而使用ThreadLocal的程序员大多只是知道其然而不知其所以然,因此,使用ThreadLo ...
- Java并发编程-线程可见性&线程封闭&指令重排序
一.指令重排序 例子如下: public class Visibility1 { public static boolean ready; public static int number; } pu ...
- java并发编程可见性与线程封闭
可见性 所谓可见性,指的是当一个线程修改了对象的状态后,其他线程能够看到该对象发生的变化.在单线程环境下,向某个变量写入值,然后在后面的操作再读取,在这个过程中该变量的值对该线程来说总是可见.但是,在 ...
- Java多线程——线程封闭
线程封闭:当访问共享的可变数据时,通常需要同步.一种避免同步的方式就是不共享数据.如果仅在单线程内访问数据,就不需要同步,这种技术称为线程封闭(thread confinement) 线程封闭技术一 ...
- 《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性
这章的主要内容是:如何共享和发布对象,从而使它们能够安全地由多个线程同时访问. 内存的可见性 确保当一个线程修改了对象状态后,其他线程能够看到发生的状态变化. 上面的程序中NoVisibility可能 ...
- Java并发编程:线程封闭和ThreadLocal详解
转载请标明出处: http://blog.csdn.net/forezp/article/details/77620769 本文出自方志朋的博客 什么是线程封闭 当访问共享变量时,往往需要加锁来保证数 ...
- Java并发编程(七)线程封闭
当访问共享的可变数据时,通常需要使用同步.一种避免使用同步的方式就是不共享数据. 如果仅在单线程内访问数据,就不需要同步.这种技术被称为线程封闭(Thread Confinement),它是实现线程安 ...
- Java 并发编程(二)对象的公布逸出和线程封闭
对象的公布与逸出 "公布(Publish)"一个对象是指使对象可以在当前作用域之外的代码中使用.可以通过 公有静态变量.非私有方法.构造方法内隐含引用 三种方式. 假设对象构造完毕 ...
- Java并发之线程封闭
读者们好! 在这篇博客中,我们将探讨线程封闭是什么意思,以及我们如何实现它. 所以,让我们直接开始吧. 1. 线程封闭 大多数的并发问题仅发生在我们想要在线程之间共享可变变量或可变状态时.如果在多个线 ...
随机推荐
- Data Base Oracle 常用命令
Data Base Oracle 常用命令 1.登录:(不需要密码,属于管理员权限) conn /as sysdba; 2.查看数据库存储位置: select name from v$datafil ...
- todolist作业涉及知识点
window.event对象详细介绍 1.event代表事件的状态,例如触发event对象的元素.鼠标的位置及状态.按下的键等等.event对象只在事件发生的过程中才有效.event的某些属性只对特定 ...
- [CF700E][JZOJ5558]Cool Slogan (后缀自动机+线段树)
题意翻译 给出一个长度为$n$的字符串$s[1]$,由小写字母组成.定义一个字符串序列$s[1....k]$,满足性质:$s[i]$在$s[i-1]$ $(i>=2)$中出现至少两次(位置可重叠 ...
- 从哈希结构去理解PHP数组
php的数组实际上就是hash_table,无论是 数字索引数组array(1, 2, 3) 还是关联数组array(1 => 2, 2=> 4)等等. 一,这里的hash_table有几 ...
- 微信小程序之tabbar切卡
最近在研究小程序的时候,遇到了一个问题,就是tabbar切卡,在android上有fragment,在RN上也有提供一个第三方的组件来用,微信小程序,好像没有专门的一个组件来实现这个功能,度娘了大半天 ...
- [Flex] 组件Tree系列 —— 阻止用户点击选中Tree中任何节点
mxml: <?xml version="1.0" encoding="utf-8"?> <!--功能描述:阻止用户点击选中Tree中任何节点 ...
- js实现抛物线
这个是很简单的一种方式,利用了css3的transition属性 <!DOCTYPE html> <html lang="en" style="widt ...
- linux上的常用的进程与内存优化命令
进程 ps命令 f 以树状结构显示 u 显示详细信息 a 显示所有进程 -A 显示所有进程 -u 用户名 是显示该用户下的进程 -l 更多进程详细信息 例子1. 以树状结构显示root用户下进程的详细 ...
- CSS3 文本溢出问题
一.单行: 效果: 实现这各效果必须要加上: text-overflow: ellipsis; /*文本溢出*/ overflow: hidden; /*配合使用:溢出隐藏*/ white-space ...
- 转 rman 恢复报错
###sample 1 原因是新库起了FRA 区,FRA 区的旧文件属于之前的归档日志文件产生,这样会导致无法识别的问题. 解决办法,清空FRA或者恢复时候不启用FRA. RMAN RESTORE ...