`
zhouchaofei2010
  • 浏览: 1104622 次
  • 性别: 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

    ### Java并发编程实践 #### 一、并发编程基础 ##### 1.1 并发与并行的区别 在Java并发编程中,首先需要理解“并发”(Concurrency)和“并行”(Parallelism)的区别。“并发”指的是多个任务在同一时间段内交替...

    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高并发程序设计第二版随书代码

    这本书涵盖了Java并发编程的核心概念和技术,旨在帮助开发者在实际项目中高效地处理高并发场景。随书附带的代码提供了丰富的示例,以便读者能够更直观地理解并实践这些理论知识。 1. **Java并发基础** - **线程与...

    Java并发编程实战1

    《Java并发编程实战1》这本书深入探讨了Java平台上的并发编程技术。本书分为四个部分,旨在帮助读者理解和掌握如何在实际应用中有效地利用多线程。 第一部分的基础知识介绍了线程的基本概念。线程是程序执行的最小...

    java并发编程实践笔记

    ### Java并发编程实践笔记知识点详解 #### 一、保证线程安全的方法 1. **不要跨线程访问共享变量:** 当多个线程共享某个变量时,若其中一个线程修改了该变量,其他线程若没有正确同步,则可能读取到错误的数据。...

    Java并发编程的艺术

    第三章专门讨论Java内存模型,这是一个对Java并发编程至关重要的主题。Java内存模型定义了程序的不同部分如何共享数据,特别是在多线程环境中。本章详细介绍了内存可见性、原子性和有序性等概念,并探讨了它们如何...

    高并发编程实战1,2,3阶段

    ### 高并发编程实战1,2,3阶段 #### 知识点概览 本系列教程分为三个阶段,旨在帮助开发者全面理解并掌握高并发编程技术。通过理论讲解与实战演练相结合的方式,深入剖析了多线程编程的核心原理及其在实际项目中的...

    汪文君JAVA多线程编程实战(完整不加密)

    本书还涉及到了Java并发工具类,如Semaphore信号量、CyclicBarrier栅栏、CountDownLatch倒计时器等,这些都是Java并发编程中的重要工具,可以帮助开发者更精细地控制线程执行。另外,书中还会介绍如何使用Future和...

    Java并发编程实践.rar

    第三章:共享数据与线程安全 这一章重点讲解了线程不安全的问题,如数据竞争、死锁、活锁和饥饿。并详细阐述了Java中的线程安全类,如Atomic类、synchronized关键字的用法,以及 volatile 关键字的作用。 第四章:...

    java 并发编程的艺术

    第3章详细介绍了这一主题,解开了Java内存模型的神秘面纱,帮助程序员更好地理解内存可见性问题及其解决方案。此外,书中还讨论了volatile关键字的实现原理,这是理解内存模型中不可忽视的一部分。 多线程编程是...

    实战Java高并发程序设计(高清版)

    5. **J.U.C框架**:Java并发 utilities (J.U.C) 框架是Java并发编程的重要组成部分,书中会介绍如何利用这个框架来提升并发性能和代码的可读性。 6. **性能调优**:在高并发场景下,性能优化是必不可少的。可能涵盖...

    JAVA开发实战经典-课后习题答案-李兴华.pdf

    - **多线程**: 原生支持多线程,简化了并发编程。 - **安全性**: Java设计时考虑到了安全性,例如数组边界检查等。 - **可移植性**: 可以在不同平台运行,提供跨平台的能力。 通过上述知识点的总结,可以了解到Java...

Global site tag (gtag.js) - Google Analytics