`
沉沦的快乐
  • 浏览: 56869 次
  • 性别: 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 并发核心编程

    - **特性**: 创建结束时,若对象被安全发布,则所有线程都可以获取到final字段在构造过程中设置的值,即使没有`synchronized`关键字。 #### 三、保护共享数据 在多线程环境中,保护共享数据至关重要,以确保数据...

    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并发编程实践笔记

    14. **对象发布的注意事项:** 发布对象时必须确保其完全构造完成,以防止对象逸出导致的线程安全问题。 15. **使用ThreadLocal确保线程封闭性:** 通过`ThreadLocal`可以在每个线程中存储独立的副本,从而避免线程...

    Java并发编程实践 PDF 高清版

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

    java并发编程实践笔记资料.pdf

    Java并发编程实践笔记是一份关于Java并发编程的实践笔记,涵盖了多种关于线程安全、并发编程的实践经验和原则。下面是从笔记中总结的知识点: 1. 保证线程安全的三种方法:不要跨线程访问共享变量,使用final类型的...

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

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

    java 并发编程的艺术

    读者对象涵盖了Java开发工程师、架构师、并发编程爱好者以及大专院校师生,因此书中内容覆盖面广泛,从基础知识到高级技术均有涉及。 在前言中,作者还谈到了个人的成长经历和写书的初衷,让读者看到了一本技术书籍...

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

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

    一本经典的多线程书籍 Java并发编程 设计原则与模式 第二版 (英文原版)

    - 安全发布:确保对象初始化完成后再被其他线程访问。 - 不可变对象:一旦创建,其状态就不能改变,天然线程安全。 - 状态封闭:确保对象的状态只在其创建者线程内修改。 - 双重检查锁定(Double-Check Locking...

    java 并发编程实践 001

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

    java并发编程实践(英文).docx

    此外,还涉及了对象共享,包括可见性、发布与逸出、线程封闭、不可变性以及安全发布。 第二部分“构造并发应用程序”则深入到任务执行、并发控制和错误处理等主题。如何在线程中执行任务,使用Executor框架,以及...

    大学毕业设计---ios平台下并发编程的研究和实现.doc

    操作队列是 iOS 平台下的并发编程技术之一。操作队列可以帮助开发者更好地管理并发任务,提高程序的性能和响应速度。操作队列的设计需要考虑到操作对象的执行行为、操作对象的子类化、自定义操作对象的执行行为等...

    java并发核心编程

    为了避免这些问题,可以通过确保对象的完全初始化来实现安全发布。例如,在构造函数中注册回调或者延迟对象的发布。 - **好处**:确保所有线程看到的是一个完整初始化的对象,从而避免潜在的并发问题。 7. **不可...

Global site tag (gtag.js) - Google Analytics