1、不变性

  某个对象在被创建后其状态就不能被修改,那么这个对象就称为不可变对象,不可变对象一定是线程安全的。不可变对象很简单。他们只有一种状态,并且该状态由构造函数来控制。

  当满足以下条件时,对象才是不可变的:(1)、对象创建以后其状态就不能改变;(2)、对象的所有域都是final类型;(3)、对象是正确创造的(在对象创建期间,this引用没有溢出)。

1.1 final域

  关键字final用于构造不可变对象,final类型的域是不能修改的(但是final域所引用的对象是可变的,那么这些引用的对象是可以修改的),即使对象是可变的,通过将可变对象的某些域声明为final类型,相当于告诉维护人员这些域是不可变化的。

2、正确发布一个对象

  正确发布一个对象遇到的两个问题:(1)引用本身要被其他线程看到;(2)对象的状态要被其他线程看到。

  在多线程编程中,首要的原则,就是要避免对象的共享,因为如果没有对象的共享,那么多线程编写要轻松得多,但是,如果要共享对象,那么除了能够正确的将构造函数书写正确外,如何正确的发布也是一个很重要的问题。

  我们看下面的代码:

 public class Client {
public Holder holder; public void initialize(){
holder = new Holder(42);
}
} public class Holder {
int n;
public Holder(int n) {
this.n = n;
}
public void assertSanity() {
if(n != n)
throw new AssertionError("This statement is false.");
}
}

  在Client类中,Holder对象被发布了,但是这是一个不正确的发布。由于可见性问题,其他线程看到的Holder对象将处于不一致的状态,即使在该对象的构成构函数中已经正确的该构建了不变性条件,这种不正确的发布导致其他线程看到尚未创建完成的对象。主要是Holder对象的创建不是原子性的,可能还未构造完成,其他线程就开始调用Holder对象。

由于没有使用同步的方法来却确保Holder对象(包含引用和对象状态都没有)对其他线程可见,因此将Holder成为未正确发布。问题不在于Holder本身,而是其没有正确的发布。上面没有正确发布的可能导致的问题:

  • 别的线程对于holder字段,可能会看到过时的值,这样就会导致空引用,或者是过时的值(即使holder已经被设置了)(引用本身没有被别的线程看到)
  • 更可怕的是,对于已经更新holder,及时能够看到引用的更新,但是对于对象的状态,看到的却可能是旧值,对于上面的代码,可能会抛出AssertionError异常

主要是holder = new Holder(42);这个代码不是原子性的,可能在构造未完成时,其他线程就会调用holder对象引用,从而导致不可预测的结果。

2.1安全发布常用模式

  要安全的发布一个对象,对象的引用和对象的状态必须同时对其他线程可见。一般一个正确构造的对象(构造函数不发生this逃逸),可以通过如下方式来正确发布:

  (1)、在静态初始化函数中初始化一个对象引用

  (2)、将一个对象引用保存在volatile类型的域或者是AtomicReference对象中

  (3)、将对象的引用保存到某个正确构造对象的final类型的域中。

  (4)、将对象的引用保存到一个由锁保护的域。

  

  在线程安全容器内部同步意味着,在将对象放到某个容器中,比如Vector中,将满足上面的最后一条需求。如果线程A将对象X放到一个线程安全的容器中,随后线程B读取这个对象,那么可以确保可以确保B看到A设置的X状态,即便是这段读/写X的应用程序代码没有包含显示的同步。下面容器内提供了安全发布的保证:

  (1)、通过将一个键或者值放入Hashtable、synchronizedMap或者ConcurrentMap中,可以安全将它发布给任何从这些容器中访问它的线程。

  (2)、通过将某个元素放到Vector、CopyOnWriteArrayList、CopyOnWriteArraySet、synchroizedList,可以将该元素安全的发布到任何从这些容器中访问该元素的线程。

  (3)、通过将元素放到BlockingQueue或者是ConcrrentLinkedQueue中,可以将该元素安全的发布到任何从这些访问队列中访问该元素的线程。

  通常,要发布一个静态构造的对象,最简单和最安全的方式是使用静态初始化器: public static Holder = new Holder(42);

  静态初始化器由JVM在类的初始化阶段执行,由于JVM内部存在同步机制,所以这种方式初始化对象都可以被安全的发布。对于可变对象,安全的发布之时确保在发布当时状态的可见性,而在随后的每次对象的访问时,同样需要使用同步来确保修改操作的可见性。

  

Java多线程——不变性与安全发布的更多相关文章

  1. Java多线程之构造与发布

    资料来源 http://www.ibm.com/developerworks/library/j-jtp0618/ http://www.javaspecialists.eu/archive/Issu ...

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

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

  3. Java多线程编程详解

    转自:http://programming.iteye.com/blog/158568 线程的同步 由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题.Ja ...

  4. Java多线程编程(4)--线程同步机制

    一.锁 1.锁的概念   线程安全问题的产生是因为多个线程并发访问共享数据造成的,如果能将多个线程对共享数据的并发访问改为串行访问,即一个共享数据同一时刻只能被一个线程访问,就可以避免线程安全问题.锁 ...

  5. Java多线程系列目录(共43篇)

    最近,在研究Java多线程的内容目录,将其内容逐步整理并发布. (一) 基础篇 01. Java多线程系列--“基础篇”01之 基本概念 02. Java多线程系列--“基础篇”02之 常用的实现多线 ...

  6. 关于JAVA多线程的那些事__初心者

    前言 其实事情的经过也许会复杂了点,这事还得从两个月前开始说.那天,我果断不干IT支援.那天,我立志要做一个真正的程序猿.那天,我26岁11个月.那天,我开始看Android.那天,我一边叨念着有朋自 ...

  7. Java最重要的21个技术点和知识点之JAVA多线程、时间处理、数据格式

    (四)Java最重要的21个技术点和知识点之JAVA多线程.时间处理.数据格式  写这篇文章的目的是想总结一下自己这么多年JAVA培训的一些心得体会,主要是和一些java基础知识点相关的,所以也希望能 ...

  8. 15个顶级Java多线程面试题及答案

    1)现在有T1.T2.T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行? 这个线程问题通常会在第一轮或电话面试阶段被问到,目的是检测你对”join”方法是否熟悉.这个多线程问题比 ...

  9. 50个Java多线程面试题

    不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题.Java 语言一个重要的特点就是内置了对并发的支持,让 Java 大受企业和程序员的欢迎.大多数待遇丰厚的 Java 开发职位都要求开发者 ...

随机推荐

  1. 探究算子find_shape_model中参数MaxOverlap的准确意思

    基于形状的模板查找算子: find_shape_model(Image : : ModelID, AngleStart, AngleExtent, MinScore, NumMatches, MaxO ...

  2. oracle 建立表空间和用户

    .建立表空间, create tablespace "NETHRA" DATAFILE 'D:\DataBase\Oracle\iClass\iclass\iclass.DBF' ...

  3. FW:考查嵌入式C开发人员的最好的16道题(转)

    考查一个初级嵌入式系统开发人员的C基本功,附有答案题目由资深嵌入式系统专家拟定, 目的是考查入门级的嵌入式软件开发人员 Gavin Shaw提供详细解答. 编者按:非常基本关于C语言的问题,一个信息类 ...

  4. guestfish修改镜像内容

    1.安装guestfish yum install libguestfs-tools 注意,如果要修改windows镜像需要安装 yum install libguestfs-winsupport 2 ...

  5. openfire搭建spackweb在线即时聊天

    1.首先去openFire官网下载openFire以及spackweb,以下地址可以2样东西一次打包下载.http://download.csdn.net/detail/a315157973/8048 ...

  6. CentOS 6.0下phpvod搭建教程(LAMP+phpvod)

    之所以安装CentOS是因为之前试过RedHat,但是发现RedHat在安装时,无法获取安装源,原因是RedHat系统没有在RHN注册. 网上的很多教程都说可以直接换用CentOS的源,可我小搞里一会 ...

  7. [label][转载][web-design-psychology]网页设计心理

    原文出处: http://mux.alimama.com/posts/1301 Tip1:信息不要同时全部展示,阶段性地向用户展示当前场景里必要的信息 设计师经常犯的错误:同时将大量信息展示给用户.不 ...

  8. JavaScript中的垃圾回收和内存泄漏

    摘要: JS内存管理. 作者:浪里行舟 Fundebug经授权转载,版权归原作者所有. 前言 程序的运行需要内存.只要程序提出要求,操作系统或者运行时就必须供给内存.所谓的内存泄漏简单来说是不再用到的 ...

  9. 自定义Team Foundation Server (TFS) 与Project Professional的集成字段

    用户可以象使用Office Excel一样,使用Project Professional连接TFS,将数据下载到本地修改,并且发布到TFS服务器上,如果你习惯使用Project来计划你的项目,那么Pr ...

  10. MYC编译器源码分析之程序入口

    前文.NET框架源码解读之MYC编译器讲了MyC编译器的架构,整个编译器是用C#语言写的,上图列出了MyC编译器编译一个C源文件的过程,编译主路径如下: 首先是入口Main函数用来解析命令行参数,读取 ...