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. 线程封闭 大多数的并发问题仅发生在我们想要在线程之间共享可变变量或可变状态时.如果在多个线 ...
随机推荐
- python excel处理
#!/usr/bin/python # data:2018/4/20 # user:fei # -*- coding: utf-8 -*- import json import sys import ...
- day03.1-函数编程
python中函数的定义: def test (x,y): "The function definitions" z = x**y return z ""&qu ...
- SQLMAP 基础操作
SQLMAP 基础操作 sudo git clone https://github.com/sqlmapproject/sqlmap GET 请求: -u POST 请求: Option: --dat ...
- windows下安装ubuntu15.04
本文主要介绍windows下安装ubuntu15.04,对与其他的版本也是适用的.现在要讲的是一种最简单ubuntu的安装方式. 1软件下载 1.磁盘分区工具DiskGenius 2.启动项修改工具E ...
- python3入门之函数
相信大家学习过其他语言,懂得函数的作用.简单的说函数最好的作用就是可以使程序变得懒惰.python作为后起之秀,当然也会拥有函数这个有用的东西: 创建函数 使用def语句即可创建函数,如创建一个用来生 ...
- h5聊天工具的开发过程及思路
这个产品的主要技术栈有,网易nim即时通信,vue-cli,muse-ui 1.在拿到这个需求时,脑袋里空的,什么想法都没有,完全懵逼,进了网易云通信的官网api查看,由于我做的是客户端的,所以重点看 ...
- javascript canvas画订单
前段时间看了某个平台的后台,发现订单显示使用的canvas进行绘画(插件echarts),直观,明了的表达出了订单的走势如下 所以自己心痒痒的,就自己模仿了一个-->贴上代码 <style ...
- 提交app时候遇到IDFA警告
1.最近提交app时候遇到如下问题,解决方案: Everything has come to its usual state now. Simply upload your binary as you ...
- 关闭tensorflow运行时的警告信息
执行简单的矩阵相乘的程序: import tensorflow as tf m1 = tf.constant([[3,3]]) m2 = tf.constant([[2],[3]]) product ...
- 进阶篇:5.3.1)均方根法(Root-Sum-Squares,RSS)
本章目的:了解均方根法,运用均方根法. 1.定义 均方根法(Root-Sum-Squares,RSS):均方根法是统计分析法的一种,是把尺寸链中的各个尺寸公差的平方之和再开根即得到关键尺寸的公差. 其 ...