java并发编程实战-第3章-对象的共享
如何共享和发布对象,使其能符合第2章所讨论的多个线程下的安全性访问。2-3章是第4章构建线程安全类
?
和通过java.util.concurrent类库来构建并发应用程序的重要基础
?
同步除了实现原子性操作外,另一个重要方面内存可见性
?
?
3.1可见性
?
? In order to ensure visibility of memory writes across threads, you must use synchronization.
?
? public class NoVisibility {
? ? private static boolean ready;
? ? private static int number;
?
? ? private static class ReaderThread extends Thread {
? ? ? ? public void run() {
? ? ? ? ? ? while (!ready)
? ? ? ? ? ? ? ? Thread.yield();
? ? ? ? ? ? System.out.println(number);
? ? ? ? }
? ? }
?
? ? public static void main(String[] args) {
? ? ? ? new ReaderThread().start();
? ? ? ? number = 42;
? ? ? ? ready = true;
? ? }
}
?
?
在没有同步的情况下,无法保证主线程写入的值对读线程是可见的(虽然如上代码测试几次可能都会输出42
?
)
?
在没有同步的情况下,编译器、处理器以及运行时都可能对执行顺序做调整(重排序)
?
3.1.1 失效数据
?
NoVisibility 可能产生失效数据,可能输出错误的结果、可能引起死循环、意料外的异常、被破坏的数据
?
结构等等
?
3.1.2 非原子的64位操作
?
? ?对于64位的long、double操作。jvm 允许拆分成2个32位的操作。所以在多个线程的读写下即使不考虑数
?
据失效、也是不安全的。除非使用volatile声明或者用锁保护
? ?
3.1.3 加锁与可见性
? ? ? ? ? Locking is not just about mutual exclusion; it is also about memory visibility
?
3.1.4 volatile
? ? ? When a field is declared volatile, the compiler and runtime are put on notice that this?
?
variable is shared and that operations on it should not be reordered with other memory?
?
operations.
? ? ? Volatile variables are not cached in registers or in caches where they are hidden from?
?
other processors, so a read of a volatile variable always returns the most recent write by any?
?
thread
?
? ? 即:1、不重排序 2、不缓存在寄存器或其他处理器不可见的地方
? ??
? ??
? ? volatile只能保证可见性,但不保证++count 的原子性。(原子变量提供了读-写-改的原子操作,通常
?
是一种更好的volatile变量)
?
? ?You can use volatile variables only when all the following criteria are met:(使用volatiel
?
,以下3个条件都要买足)
?
Writes to the variable do not depend on its current value, or you can?
?
ensure that only a single thread ever updates the value;
?
The variable does not participate in invariants with other state?
?
variables; and
?
Locking is not required for any other reason while the variable is?
?
being accessed.
? ??
? ??
? ? 适用场景:
? ? ?1、作为状态标志
? ? ?volatile boolean asleep;
? ? ? ...
? ? while (!asleep)
? ? ? ? countSomeSheep();
? ? ? ??
3.2 发布和逸出
? ? 发布:使对象在当前代码之外仍旧可用。比如存储对象的应用使其可以别的代码可以访问、通过非私有
?
方法返回、将引用传递给其他类的方法
? ? 逸出:An object that is published when it should not have been is said to have escaped.
? ??
? ? 逸出例子:
? ? 例子1:共有静态的变量
? ?Listing 3.5. Publishing an Object.
public static Set<Secret> knownSecrets;
?
public void initialize() {
? ?knownSecrets = new HashSet<Secret>();
}
?
? ? 如上共有静态的变量 knownSecrets,就是在没有initialize()完成,其它线程也能访问
? ??
? ? 例子2:使内部的可变状态逸出
? ? class UnsafeStates {
? ? private String[] states = new String[] {
? ? ? ? "AK", "AL" ...
? ? };
? ? public String[] getStates() { return states; }
? ? }
? ?
? ??
? ? 例子3:在public构造函数中发布内部类(发布的EventListener含有ThisEscape的this的隐式引用,但
?
ThisEscape确没有构造完成)
? ? public class ThisEscape {
? ? public ThisEscape(EventSource source) {
? ? ? ? source.registerListener(
? ? ? ? ? ? new EventListener() {
? ? ? ? ? ? ? ? public void onEvent(Event e) {
? ? ? ? ? ? ? ? ? ? doSomething(e);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? });
? ? ?}
? ?}
? ?
? ?需要当且仅当构造函数返回时,对象才处于可预测的和一致性状态
? ?所以,可以如下正确的发布:
? ?使用工厂方法防止this引用在构造过程中逸出
? ?
? public class SafeListener {
? ?private final EventListener listener;
?
? ?private SafeListener() {
? ? ? ?listener = new EventListener() {
? ? ? ? ? ?public void onEvent(Event e) {
? ? ? ? ? ? ? ?doSomething(e);
? ? ? ? ? ?}
? ? ? ?};
? ?}
?
? ?public static SafeListener newInstance(EventSource source) {
? ? ? ?SafeListener safe = new SafeListener();
? ? ? ?source.registerListener(safe.listener);
? ? ? ?return safe;
? ?}
}
?
?
3.3 线程封闭
? ??
? ? 如果仅在单线程里访问数据,则不需要同步。
? ? 应用例子:
? ? swing中大量使用线程封闭技术, ? swing常见错误,其他线程使用了该被封闭的对象
? ? jdbc的Connection对象,web服务器的请求线程从连接池中获取一个connection对象,直到使用完后返
?
回给连接池。在此期间,不会把connection分配给其他线程
? ? ThreadLocal :struts2等框架,把请求参数通过ThreadLocal放在封闭在自己线程中
? ??
? ? 方式有Ad-hoc、栈封闭、和ThreadLocal
? ??
? ? (5.3.2 的串行线程封闭是否属于Ad-hoc呢)
? ??
3.3.1 Ad-hoc 线程封闭(Ad-hoc拉丁语 for this 。专门的、特别设定的)
? ? ?封闭的职责完全由程序来负责,很脆弱
3.3.2 栈封闭
3.3.3 ThreadLocal类
?
? ? ? 例子:
? ? ? ?private static ThreadLocal<Connection> connectionHolder
? ?= new ThreadLocal<Connection>() {
? ? ? ?public Connection initialValue() {
? ? ? ? ? ?return DriverManager.getConnection(DB_URL);
? ? ? ?}
? ?};
?
public static Connection getConnection() {
? ?return connectionHolder.get();
}
?
? ?
? ? ? ? ?
? ? ? ? ?
? ? ? ? ?
? ? ? 可以看成把ThreadLocal(T)看成Map<Thread,T>,但不仅如此,提供了当线程停止后,T会被作为垃
?
圾回收
? ? ? 应用程序框架大量使用ThreadLocal。J2EE容器需要将一个事务上下文( Transaction Context )与
?
某个执行的线程关联起来
? ? ? 开发人员经常滥用ThreadLocal ,ThreadLocal 类似全局变量,它能降低代码的重用性。引入耦合。
?
操作要额外小心
? ? ??
? ? ? 扩展:
? ? ? spring DAO模板等底层技术,ThreadLocal是必须攻克的
? ? ??
? ? ??
? ? ??
3.4 不变性
? ? 3.4.1 Final域
? ? 3.4.2 示例:使用Volatile类型发布不可变对象
3.5 安全发布
?
? ?Immutable objects can be used safely by any thread without additional synchronization, even?
?
when synchronization is not used to publish them.
? ?
3.5.3 安全发布常用模式
? ? ? 可变对象必须通过安全的方式发布的,发布的
? ? ?To publish an object safely, both the reference to the object and the object‘s state must?
?
be made visible to other threads at the same time. A properly constructed object can be safely?
?
published by:
?
Initializing an object reference from a static initializer;(
怎么理解?
public static Holder holder = new Holder(42);
Static initializers are executed by the JVM at class initialization?
?
time; because of internal synchronization in the JVM, this mechanism is guaranteed to safely?
?
publish any objects initialized in this way [JLS 12.4.2].
? ? ? )
?
Storing a reference to it into a volatile field or AtomicReference;
?
Storing a reference to it into a final field of a properly constructed?
?
object; or
?
Storing a reference to it into a field that is properly guarded by a?
?
lock.
?
?
3.5.4 事实不可变对象
? ? ? 技术上可变,事实上不可变
? ? ??
3.5.5 ?可变对象
?
The publication requirements for an object depend on its mutability:
?
Immutable objects can be published through any mechanism;
?
Effectively immutable objects must be safely published;
?
Mutable objects must be safely published, and must be either?
?
threadsafe or guarded by a lock(3.5.3介绍的安全发布模式)
3.5.6 ?安全的共享对象,即最后如下总结
?
?
总结:
??
? The most useful policies for using and sharing objects in a concurrent program are:
?
1、线程封闭 Thread-confined. A thread-confined object is owned exclusively by and?
?
confined to one thread, and can be modifled by its owning thread.
2、只读共享 Shared read-only. A shared read-only object can be accessed concurrently?
?
by multiple threads without additional synchronization, but cannot be modified by any thread.?
?
Shared read-only objects include immutable and effectively immutable objects.
3、线程安全的对象的共享 Shared thread-safe. A thread-safe object performs?
?
synchronization internally, so multiple threads can freely access it through its public?
?
interface without further synchronization.
4、保护对象 Guarded. A guarded object can be accessed only with a specific lock held.?
?
Guarded objects include those that are encapsulated within other thread-safe objects and?
?
published objects that are known to be guarded by a specific lock.
?
?
?
?
?
?
原文:http://zhouchaofei2010.iteye.com/blog/2243277