并发处理的广泛应用是使得Amdahl定律代替摩尔定律成为计算机性能发展源动力的根本原因,也是提高计算机运算能力的最强力的武器。
《Java Concurrenccy In Practice》作者Brian Goetz对“线程安全”有一个比较合理而恰当的定义:“当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的”。
这里讨论的线程安全,限定于多个线程之间存在共享数据访问这个前提,因为如果一段代码根本不会与其他线程共享数据,那么从线程安全的角度来看,程序是串行执行还是多线程执行对它来说是完全没有区别的。
为了更加深入理解线程安全,在这里我们可以不把线程安全当作一个非真即假的二元排他选项来看待,按照线程安全的“安全程度“由强至弱来排序,我们可以将Java语言中各种操作共享的数据分为一下5类:不可变,绝对线程安全,相对线程安全,线程兼容和线程对立。
1.不可变
在Java语言中(特指JDK1.5以后,即Java内存模型被修正之后的Java语言),不可变(Immutable)的对象一定是线程安全的,无论是对象的方法实现还是方法的调用者,都不需要在再采取任何的线程安全保障措施,只要一个不可变的对象被正确地构建出来(没有发生this引用逃逸的情况),那其外部的可见状态永远也不会改变,永远也不会看到它在多个线程之中处于不一致的状态。”不可变“ 带来的安全性是最简单和最纯粹的。
保证对象行为不影响自己状态的途径有很多种,其中最简单的就是把对象种带有状态的变量都声明为final,这样在构造函数结束之后,它就是不可变的,。
2.绝对线程安全
绝对的线程安全满足Brian Goetz给出的线程安全的定义,这个定义其实是很严格的,一个类要达到”不管运行时环境如何,调用者都不需要任何额外的同步措施“通常需要付出很大的,甚至有时候是不切实际的代价。在Java API中标注自己是线程安全的类,大多数都不是绝对的线程安全。比如说java.util.Vector是一个线程安全的容器,这是所有的Java程序员都认同的。因为它的add(),get()和size()这类方法都是被synchronized修饰的,尽管这样效率很低,但的确是安全的。但是,虽然它所有的方法都被修饰成同步,也不意味着调用它的时候永远都不再需要同步手段了。看下代码
运行结果
从中可以知道,如果另一个线程恰好在错误的时间里删除了一个元素,导致序号i已经不在可用的话,再用i访问数组就会抛出一个ArrayIndexOutOfBoundsException.解决方案:对vector变量进行加同步锁处理。如下:
3.相对线程安全
相对的线程安全就是通常意义上所讲的线程安全,它需要保证对这个对象单独的操作是线程安全的,我们在调用的时候不需要做额外的保障措施,但是对于一些特定顺序的连续调用,就可能需要在调用端使用额外的同步手段来保证调用的正确性。例如上面代码中java.util.Vector类的使用就是一个非常好的例子。
4.线程兼容
线程兼容是指对象本身并不是线程安全的,但是可以通过在调用端正确地使用同步手段来保证对象在并发环境中可以安全地使用,我们平常说 一个类不是线程安全的,绝大多数时候指的是这一种情况。Java API中大部分的类都是属于线程兼容的,如与前面的Vector和HashTable相对应的集合类·ArrayList和HashMap等。
5.线程对立
线程对立是指无论调用者是否采取了同步措施,都无法在多线程环境中并发使用的代码。由于Java语言天生就具备多线程特性,线程对立这种排斥多线程的代码是很少出现的,而且通常都是有害的,应当尽量避免。
线程安全的实现方法
1.互斥同步
同步是指在多个线程并发访问共享数据时,保证共享数据在同一时刻只被一个(或者是一些,使用信号量的时候)线程使用。而互斥是实现同步的一种手段,临界区,互斥量和信号量都是主要的互斥实现方式。在这4个字里面,互斥是因,同步是果;互斥是方法,同步是目的。
在虚拟机规范对monitore和monitorexit的行为描述中,有两点是需要特别注意的。首先,synchronized同步块对同一条线来说是可重入的,不会出现自己把自己锁死的问题。其次,同步块在已进入的线程执行完之前,会阻塞后面其他线程的进入。对于代码简单的同步块(如被synchronized修饰的getter()或setter()方法),状态转换消耗的时间有可能比用户代码执行的时间还要长。所以synchronized是Java语言中一个重量级(Heavyweight)的操作,有经验的程序员都会在确实必要的情况下才使用这种操作,而虚拟机本身也会进行一些优化,譬如在通知操作系统阻塞先线程之前加入一段自旋等待过程,避免频繁地切入到核心态之中。
2.非阻塞同步
3.无同步方案