`
沉沦的快乐
  • 浏览: 55817 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

并发编程之安全发布对象

阅读更多

 对象发布(publish)

       发布一个对象是使它能够在被当前范围之外的代码所使用。比如创建一个对象之后,提供一个非私有方法返回这个对象的引用,或者把它传递到其他类的方法中。下面是常见的发布对象的例子:

1.把对象的引用保存到静态公有域中
public static List<Object> objectList;
 public void initList() {
      objectList =New ArrayList<Object>();
}

    在上面的代码中,任何类和线程都可以访问objectList,当objectList通过initList方法实例化一个ArrayList对象后,并把引用赋值给objectList,那么这个ArrayList对象就发布出去了。同时发布一个对象还可能间接的发布了其他对象。比如objectList添加了一个object1,那么object1也被发布出去了。因为任何类或线程都可以通过遍历objectList访问并修改object1。

 

2.通过非私有方法把对象发布出去
     private String[] strArray = {"data1","data2"};
     public String[] getStrArray () {
           return strArray ;
     }
     

   通过getStrArray方法,任何类或者线程都可以访问和修改strArray,此时strArray对象也被发布出去了。但是这里strArray变量定义为私有对象,通过getStrArray方法,使得strArray超出了它的作用范围,由私有变成公有的了。

   发布一个对象,同样发布了该对象的所有非私有域引用的对象,甚至整个非私有域的引用链中的对象,都发布出去了。

对象逸出(escape)

    一个对象超出了她应有的作用域,或者未被构造完全就发布了,称为对象逸出。很多情况下我们不希望对象以及对象的内部属性不被暴露出去,但是在其他一些情况中,我们又必须使对象发布出去,但是如果不经意中把对象的内部状态也发布出去了,就使得其他线程可以修改这个对象的内部状态,造成不可预知的线程安全性风险,同时,如果对象没有构造完全就发布出去了同样会危及线程安全。上面第2个代码就是把私有对象发布出去而导致对象逸出。下面是一种对象未构造完全就发布造成的对象逸出:

对象在构建期间逸出
public class ThisEscape() {
     public ThisEscape(EventSource event) {
        event.register(new EventListener(){
            public void onEvent(Event e){ 
                  doSomeThing(e);}
         })     
    }
}

      这段代码的本意是注册监听器,通过匿名内部类发布EventListener,但是由于内部类会持有封装它的外部类的引用this,所以ThisEscape的一个对象也发布出去了。从而导致对象未构造完全就发布出去的逸出。对象只有在构造函数返回后才处于可预知的,真正稳定的状态。所以从构造方法中发布对象,它发布的对象是未构建全的对象,即使是在对象的最后一行发布也是如此。一个非常常见的在构造期对象逃逸的错误,是在构造期启动线程,无论是显式的还是隐式的(因为thread或ruanable是所属对象的内部类),this引用总是被新线程所持有。

如何安全发布对象

     为了安全的发布对象,对象的引用以及对象的状态必须同时对其他线程可见。那么如何才能安全的发布对象呢,下面是正确发布对象的几种方法

1.通过静态初始化器初始化对象的引用

  常见的例子是在构造方法中注册监听器或启动线程。如果非要在构造函数中启动线程或启动线程,可以使用私有的构造函数和公有的工厂方法,比如上面注册监听器的例子:

public class SafeListener() {
     private  final EventLister listener;
     
      private SafeListener() {
            listener= new EventLister () {
                    public void onEvent(Event e) {
                            doSomething(e);
                   }
              }
               
      }
      
      public static SafeListener getInstance(EventSource source) {
            SafeListener  safeListener = new SafeListener();
            source.resgister(safeListener );
            return safeListener ;
      }
       
}

 2.线程封闭

      需要线程同步的基础是线程间共享对象和数据,如果线程不共享数据和对象,对象只封闭在线程内部,那么就不需要线程同步,也就不需要发布对象,即使对象是不线程安全的。

2.1 线程限制

      通过编程人员维护对象安全发布,而没有变量修饰词或线程本地变量把对象限制在线程上。这种方法非常容易出错,因此也叫非正式线程限制(Ad-hoc线程限制)。下面是几种常见的线程限制方法:

volatile变量

一张典型的线程限制是使用volatile关键词。只要你确保只要一个线程修改volatile变量,那么volatile变量的读取-修改-写入操作就限制在一个线程中,避免了线程的竞争条件。并且volatile的可见性可以保证其他线程能看到变量的最新值,因此是线程安全的。

栈限制

    即使变量的作用域只局限于线程内部,比如局部变量。

2.3 threadlocal

      threadlocal允许每个线程与对象关联在一起,它提供了set/get方法,为每个使用它的线程维护一份单独的拷贝。threadlocal通常使用在防止基于可变的单例或全局变量中出现不正确的共享。

2.4 不变对象

     不变对象一旦被创建就不能更改,所以永远是线程安全的。所谓的不变对象,必须满足3个条件:创建后状态不能更改,所有域必须是final的,被正确构建(构造期间没有发生对象逸出)。不变对象一般是一些比较简单的对象,它的内部状态在对象构造方法里就赋值了。

3.将对象的引用存储到被正确同步的变量或对象中

   具体有下面几种做法:

   将对象的引用存储到volatile域或AutomaticReference中;

   将对象的引用存储到正确创建对象的final域中

   将对象的引用存储到被锁正确保护的域中,比如线程安全的容器 vector,synchronizedMap,concurrentMap,synchronizedList,synchronizedSet,BlockingQueue,concurrentLinkedQueue等。

 

 

分享到:
评论

相关推荐

    Java并发编程实战

    3.5.3 安全发布的常用模式 3.5.4 事实不可变对象 3.5.5 可变对象 3.5.6 安全地共享对象 第4章 对象的组合 4.1 设计线程安全的类 4.1.1 收集同步需求 4.1.2 依赖状态的操作 4.1.3 状态的所有权 4.2 实例...

    Java 并发编程实战

    3.5.3 安全发布的常用模式 3.5.4 事实不可变对象 3.5.5 可变对象 3.5.6 安全地共享对象 第4章 对象的组合 4.1 设计线程安全的类 4.1.1 收集同步需求 4.1.2 依赖状态的操作 4.1.3 状态的所有权 4.2 实例...

    Java并发编程实践 PDF 高清版

    本书的读者是那些具有一定Java编程经验的程序员、希望了解Java SE 5,6在线程技术上的改进和新特性的程序员,以及Java和并发编程的爱好者。 目录 代码清单 序 第1章 介绍 1.1 并发的(非常)简短历史 1.2 线程的...

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

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

    Java并发编程及高并发解决方案

    第5章安全发布对象 第6章线程安全策略 第7章J.U.C之AQS 第8章J.U.C组件拓展 第9章线程调度-线程池 第10章多线程并发拓展 第11章高并发之扩容思路 第12章高并发之缓存思路 第13章高并发之消息队列思路 第14章高并发之...

    java 并发编程实践 001

    java 并发编程实践001 002 两个文件全部下载后 用 7z解压 第1章 介绍 1.1 并发的(非常)简短历史 1.2 线程的优点 1.3 线程的风险 1.4 线程无处不在 第1部分 基础 第2章 线程安全 2.1 什么是线程安全性 2.2 原子...

    JAVA并发编程实践_中文版(1-16章全)_1/4

    3.5 安全发布 . 第4章 组合对象 4.1 设计线程安全的类 4.2 实例限制 4.3 委托线程安全 4.4 向已有的线程安全类添加功能 4.5 同步策略的文档化 第5章 构建块 5.1 同步容器 5.2 发容器 5.3 阻塞队列和生产者一消费者...

    Java并发编程part2

    中文完整版的Java并发编程实践PDF电子书 作者:Brian Gogetz Tim Peierls Joshua Bloch Joseph Bowbeer David Holmes Doug Lea 译者:韩锴 方秒 目录 第1章 介绍 1.1 并发的(非常)简短历史 1.2 线程的优点 1.3 ...

    Java并发编程实践part1

    中文完整版的Java并发编程实践PDF电子书 作者:Brian Gogetz Tim Peierls Joshua Bloch Joseph Bowbeer David Holmes Doug Lea 译者:韩锴 方秒 目录 第1章 介绍 1.1 并发的(非常)简短历史 1.2 线程的优点 1.3 ...

    并发:高并发编程学习与实战

    同时高并发编程学习与实战,系统的学习并发编程的知识。目录结构atomic:原子类container:同步容器与并发容器不可变:发布:对象的发布sync:线程同步threadLocal:螺纹封闭unsafe-class:常用的线程不安全类

    精通Windows.API-函数、接口、编程实例.pdf

    仅收录该书籍以供学习和讨论 包含pdf书籍及经过验证的示例 执行demo中的示例方式 在编译环境下进入demo目录,执行...17.2.3 列举安全对象的安全描述符 515 17.2.4 修改安全描述符 521 17.3 用户 522 17.3.1 ...

    记录各种学习笔记(算法、Java、数据库、并发......).zip

    多线程支持:Java内建对多线程编程的支持,使并发编程变得更加简单直接。 动态性:Java可以通过反射、注解等机制实现在运行时动态加载类和修改行为,增加了程序的灵活性。 综上所述,Java凭借其强大的特性和广泛...

    精通WindowsAPI 函数 接口 编程实例

    此书电子版的共547页,本人分卷上传300页,后面部分将陆续...17.2.3 列举安全对象的安全描述符 515 17.2.4 修改安全描述符 521 17.3 用户 522 17.3.1 创建用户 522 17.3.2 用户组 523 17.3.3 删除用户 525...

    Java面试整理,涵盖基础、JVM、线程并发、框架、MySQL、微服务、Redis、中间件、数据结构与算法等。陆续完善中.zip

    多线程支持:Java内建对多线程编程的支持,使并发编程变得更加简单直接。 动态性:Java可以通过反射、注解等机制实现在运行时动态加载类和修改行为,增加了程序的灵活性。 综上所述,Java凭借其强大的特性和广泛...

    收集优秀的编程资源(Java为主).zip

    多线程支持:Java内建对多线程编程的支持,使并发编程变得更加简单直接。 动态性:Java可以通过反射、注解等机制实现在运行时动态加载类和修改行为,增加了程序的灵活性。 综上所述,Java凭借其强大的特性和广泛...

    VC与Labview、Matlab编程论文资料[2].rar

    这是VC与Labview、Matlab编程论文资料,全部为pdf格式,讲述有VC编程技巧、也有VC与Labview混合编程, VC与matlab混合编程,有365个,约500M,分成4个【独立】压缩包,如果想要全部的论文则需要下载全部压 缩包,...

    CS 自学指南(Java编程语言、数据库、数据结构与算法、计算机组成原理、操作系统、计算机网络、英语、简历、面试).zip

    多线程支持:Java内建对多线程编程的支持,使并发编程变得更加简单直接。 动态性:Java可以通过反射、注解等机制实现在运行时动态加载类和修改行为,增加了程序的灵活性。 综上所述,Java凭借其强大的特性和广泛...

    geeteventbus:用于高度并发的Python应用程序的进程内事件总线

    geeteventbus用于并发编程的事件总线geeteventbus是一个允许发布-订阅式通信的库。 组件无需彼此注册。 它受Java库(来自Google的Guava eventbus)的启发。 但这与Guava事件总线库并不完全相同。 geeteventbus简化了...

Global site tag (gtag.js) - Google Analytics