`
zhouchaofei2010
  • 浏览: 1086332 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

java并发编程实战-第3章-对象的共享

阅读更多

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.

 

 

 

 

 

 

0
1
分享到:
评论

相关推荐

    《java并发编程实战》读书笔记-第3章-对象的共享

    《java并发编程实战》读书笔记-第3章-对象的共享,脑图形式,使用xmind8制作 包括可见性、发布与逸出、线程封闭、不可变性、安全发布等内容

    Java并发编程实战

    第3章 对象的共享 3.1 可见性 3.1.1 失效数据 3.1.2 非原子的64位操作 3.1.3 加锁与可见性 3.1.4 Volatile变量 3.2 发布与逸出 3.3 线程封闭 3.3.1 Ad-hoc线程封闭 3.3.2 栈封闭 3.3.3 ThreadLocal类 ...

    《java并发编程实战》读书笔记-第4章-对象的组合

    《java并发编程实战》读书笔记-第3章-对象的共享,脑图形式,使用xmind8制作 包括线程安全类设计、实例封闭、线程安全性委托、现有线程安全类中添加功能和文档化同步策略等内容

    Java并发编程实战2019.zip

    Java并发编程实战,第1章 简介,第2章 线程安全性 第3章 对象的共享 第4章 对象的组合 第5章 基础构建模块 第6章 任务执行 第7章 取消与关闭 第8章 线程池的使用 第9章 图形用户界面应用程序 第10章 避免...

    《java并发编程实战》读书笔记-第5章-基础构建模块

    《java并发编程实战》读书笔记-第3章-对象的共享,脑图形式,使用xmind8制作 包括同步容器类、并发容器类、阻塞队列和生产者消费者模式、阻塞和中断方法、同步工具类。最后是构建高效且可伸缩的结果缓存

    Java 并发编程实战

    第3章 对象的共享 3.1 可见性 3.1.1 失效数据 3.1.2 非原子的64位操作 3.1.3 加锁与可见性 3.1.4 Volatile变量 3.2 发布与逸出 3.3 线程封闭 3.3.1 Ad-hoc线程封闭 3.3.2 栈封闭 3.3.3 ThreadLocal类 3.4...

    Java并发编程实践 PDF 高清版

    第3章 共享对象 3.1 可见性 3.2 发布和逸出 3.3 线程封闭 3.4 不可变性 3.5 安全发布 . 第4章 组合对象 4.1 设计线程安全的类 4.2 实例限制 4.3 委托线程安全 4.4 向已有的线程安全类添加功能 4.5 同步策略的文档化 ...

    Java并发编程实战1

    第二章第二章 线程安全性线程安全性如果发现一个状态变量没有使用合适的同步,三种修复方法:不在线程之间共享该状态变量将状态变量修改为不可变的变量在访问状态变量是使

    Hibernate实战(第2版 中文高清版)

     第3章 领域模型和元数据   3.1 CaveatEmptor应用程序   3.1.1 分析业务领域   3.1.2 CaveatEmptor领域模型   3.2 实现领域模型   3.2.1 处理关注点渗漏   3.2.2 透明和自动持久化   3.2.3 编写POJO...

    asp.net知识库

    深入剖析ASP.NET组件设计]一书第三章关于ASP.NET运行原理讲述的补白 asp.net 运行机制初探(httpModule加载) 利用反射来查看对象中的私有变量 关于反射中创建类型实例的两种方法 ASP.Net应用程序的多进程模型 NET委托...

    二十三种设计模式【PDF版】

    所以很少存在简单重复的工作,加上Java 代码的精炼性和面向对象纯洁性(设计模式是 java 的灵魂),编程工作将变成一个让你时刻 体验创造快感的激动人心的过程. 为能和大家能共同探讨"设计模式",我将自己在学习中的心得...

Global site tag (gtag.js) - Google Analytics