【转载】:http://www.2cto.com/kf/201310/247738.html

前言

多线程并发环境下,线程安全极为重要。往往一些问题的发生都是由于不正确的发布了对象造成了对象逸出而引起的,因此如果系统开发中需要发布一些对象,必须要做到安全发布,以免造成安全隐患。
 
 
发布和逸出 
    所谓发布对象是指使一个对象能够被当前范围之外的代码所使用。所谓逸出是指一种错误的发布情况,当一个对象还没有构造完成时,就使它被其他线程所见,这种情况就称为对象逸出。在我们的日常开发中,经常要发布一些对象,比如通过类的非私有方法返回对象的引用,或者通过公有静态变量发布对象。如下面这些代码所示:
 class Unsafepublish {
private String[] states={"AK","AL"};
public String[] getStates(){
return states;
}
publicstaticvoid main(String[] args) {
UnSafeStates safe = newUnSafeStates();
System.out.println(Arrays.toString(safe.getStates()));
safe.getStates()[1] = "c";
System.out.println(Arrays.toString(safe.getStates()));
}
}
输出结果[AL,KL] 
     [AL,c]
    以上代码通过public访问级别发布了类的域,在类的外部任何线程都可以访问这些域,这样发布对象是不安全的,因为我们无法假设,其他线程不会修改这些域,从而造成类状态的错误。还有一种逸出是在构造对象时发生的,它会使类的this引用发生逸出,从而使线程看到一个构造不完整的对象,如下面代码所示:
public class Escape{
private int thisCanBeEscape = 0;
public Escape(){
new InnerClass();
}
private classInnerClass {
public InnerClass() {
//这里可以在Escape对象完成构造前提前引用到Escape的private变量
System.out.println(Escape.this.thisCanBeEscape);
}
}
public static void main(String[] args) {
newEscape();
}
}
    上面的内部类的实例包含了对封装实例隐含的引用,这样在对象没有被正确构造之前,就会被发布,有可能会有不安全因素。 
一个导致this引用在构造期间逸出的错误,是在构造函数中启动一个线程,无论是隐式启动线程,还是显式启动线程,都会造成this引用逸出,新线程总会在所属对象构造完毕前看到它。所以如果要在构造函数中创建线程,那么不要启动它,而应该采用一个专有的start或initialize方法来统一启动线程。我们可以采用工厂方法和私有构造函数来完成对象创建和监听器的注册,这样就可以避免不正确的创建。记住,我们的目的是,在对象未完成构造之前,不可以将其发布。 
 
 
安全发布对象 
   如果不正确的发布了可变对象,那么会导致两种错误。首先,发布线程以外的任何线程都可以看到被发布对象的过期值;其次更严重的情况是,线程看到的被发布对象的引用是最新的,然而被发布对象的状态却是过期的。如果一个对象是可变对象,那么它就要被安全发布,通常发布线程与消费线程必须同步化。一个正确创建的对象可以通过下列条件安全发布:
    1、通过静态初始化器初始化对象引用。 
    2、将发布对象的引用存储到volatile域或者具有原子性的域中(如:java5.0中的AtomicReference)。 
    3、将发布对象引用存放到正确创建的对象的final域中。 
    4、将发布对象引用存放到由锁保护的域中(如:同步化的容器)。 
 
    如果要发布一个被静态创建的对象,最简单的方式就是使用静态初始化器,如下面代码所示:public static Holder holder=new Holder();静态初始化器由JVM在类初始化时执行,JVM在执行静态变量的初始化时会有内在同步保护,因此可以保证对象的安全发布。 
 
 
高效不可变对象 
    有些对象在发布后就不会被修改,其他线程要在没有额外同步的情况下安全的访问它们,此时安全的发布就是至关重要的。所有的安全发布机制都能保证,只要一个对象在发布当时的状态对所有访问线程都可见,那么到它的引用也都可见。如果发布时的状态不会再改变,那么就必须确保任意访问是安全的。
    一个对象是可变的,但是它的状态不会在发布后被修改,这样的对象称作“高效不可变对象”。这种对象没有满足我在上一篇文章中所说的不可变对象的条件,但是这些对象在发布后可以被简单的当做不可变对象来使用,另外由于减少了同步,使用它们还会提高效率。如下面代码所示:
    public Map<String,Date> lastlogin=Collections.synchonizedMap(new HashMap<String,Date>());
    Date对象本身是可变的,每当Date被跨线程来访问都要使用锁来确保访问安全。但是此时我们却可以把它当作一个不可变对象来使用,因为我们将Date对象置入了一个线程安全的HashMap容器中,此时访问这些Date对象值就不再需要额外的同步了。因此任何线程都可以在没有额外同步的情况下安全的使用一个高效不可变对象,但前提是这些对象必须被安全发布,即必须满足上面提到的安全发布条件。 
 
 
安全地共享对象 
    现在我们来总结一下,在并发编程中的一些安全共享对象的策略。
    1、线程限制:一个被线程限制的对象,由线程独占,并且只能被占有它的线程修改。
    2、共享只读:一个共享只读的对象,在没有额外同步的情况下,可以被多个线程并发访问,但是任何线程都不能修改它。
    3、线程安全对象:一个线程安全的对象或者容器,在内部通过同步机制来保证线程安全,所以其他线程无需额外的同步就可以通过公共接口随意访问它。
    4、被守护对象:被守护对象只能通过获取特定的锁来访问

java 发布和逸出的更多相关文章

  1. Java多线程——volatile关键字、发布和逸出

    1.volatile关键字 Java语言提供了一种稍弱的同步机制,即volatile变量.被volatile关键字修饰的变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在每次读取volatit ...

  2. Java线程安全性中的对象发布和逸出

    发布(Publish)和逸出(Escape)这两个概念倒是第一次听说,不过它在实际当中却十分常见,这和Java并发编程的线程安全性就很大的关系. 什么是发布?简单来说就是提供一个对象的引用给作用域之外 ...

  3. Java并发编程(五):Java线程安全性中的对象发布和逸出

    发布(Publish)和逸出(Escape)这两个概念倒是第一次听说,不过它在实际当中却十分常见,这和Java并发编程的线程安全性就很大的关系. 什么是发布?简单来说就是提供一个对象的引用给作用域之外 ...

  4. Java并发编程(六)发布与逸出

    "发布(Publish)"一个对象的意思指,使对象能够在作用域之外的代码中使用. 例如: 将一个指向该对象的引用保存到其他代码可以访问的地方 在一个非私有的方法中返回该引用 将引用 ...

  5. 发布逸出 java this 逸出【转】

    转自:http://blog.csdn.net/joker_zhou/article/details/7322801 (1)发布:发布是指将一个对象,使其引用储存到一个其他代码可以访问到的地方,在一个 ...

  6. JAVA并发编程学习笔记------对象的可见性及发布逸出

    一.非原子的64位操作: 当线程在没有同步的情况下读取变量时,可能会得到一个失效值,但至少这个值是由之前某个线程设置的值,而不是一个随机值,这种安全性保证被称为最低安全性.最低安全性适用于绝大多数变量 ...

  7. Java 并发编程(二)对象的公布逸出和线程封闭

    对象的公布与逸出 "公布(Publish)"一个对象是指使对象可以在当前作用域之外的代码中使用.可以通过 公有静态变量.非私有方法.构造方法内隐含引用 三种方式. 假设对象构造完毕 ...

  8. this引用逸出

    1.定义 public class UnsafeClass { public UnsafeClass(Button button) { button.addActionListener(new Act ...

  9. 用JAVA中BufferedImage画出漂亮的验证码点击变化

    如果我们想用JAVA中BufferedImage画出漂亮的验证码点击变化怎么实现呢,类似这样: 点击变化,以下是实现过程,直接上代码: 首先前台:<i><img style=&quo ...

随机推荐

  1. 在Linux平台上用ASP.NET 5 连接Redis服务器

    最近在做一个Linux平台上基于ASP.Net 5 中间件+Redis+Mysql架构的系统,研究使用了 StackExchange.Redis 作为asp.net5连接redis的工具.作者在前几天 ...

  2. [转载][jQuery] Cannot read property ‘msie’ of undefined错误的解决方法

    参考 [jQuery] Cannot read property ‘msie’ of undefined错误的解决方法 ---------------------------------------- ...

  3. dedecms安装步骤

    GD支持:PHP设置-PHP扩展-php_gd2 初始化数据体验包:点击下载:或者点击取消     如果是本地安装在数据库的在数据库用户名选择默认的(root),密码为空 主要是如果是基于远程服务器的 ...

  4. PHP之文件的锁定、上传与下载

    小结文件的锁定机制.上传和下载 1.文件锁定 现在都在讲究什么分布式.并发等,实际上文件的操作也是并发的,在网络环境下,多个用户在同一时刻访问页面,对同一服务器上的同一文件进行着读取,如果,这个用户刚 ...

  5. java中的拷贝文件FileChannel

    以前用Java拷贝文件,只知道写byte数组循环拷贝,今天知道了可以用FileChannel进行拷贝,上代码: 下边是传统的byte数组拷贝方法 </pre><pre name=&q ...

  6. 第二十七篇、使用MVVM布局页面

    思路:架构的设计模式主要有这么两种 >MVC :这种方式用得很多,也很是常见,不在过多的介绍 >MVVM:使用这种 常常需要导入第三方框架,常见的是响应式框架 >主要讲一下ViewM ...

  7. C++ 泛型编程/模板 泛函编程/Lambda/λ演算

    1.泛型编程(C++模板) 其中,Ada, Delpha, Java, C#, Swift 称之为 泛型/generics; ML, Scala和 Haskell 称之为 参数多态/parametri ...

  8. 【nodemailer】 初试

    nodemailer 是什么? 简单的讲nodemailer就是用来发送邮件的.最近的一个项目需要向客户的注册邮箱发送验证连接,研究了一下. 刚开始我以为nodemailer还可以用来接收邮件,看了好 ...

  9. Linux C 程序 字符串运算符-表达式(TWO)

    1.字符串常量 双引号"" :eg:"china"   ,字符串在存储的时候会以一个\0为结束标志.2.符号常量  ,给常量取一个名字. #include< ...

  10. 正确理解javascript的this关键字

    javascript有this关键字,它和javascript的执行上下文有着密切的关系,就是说this具体指代什么要根据它的上下文来判断. 一.this和对象的关系    var Person={ ...