第13章 线程安全与锁优化

来源:互联网 发布:mac给ios手机装 编辑:程序博客网 时间:2024/06/03 06:43

线程安全

 

Java语言中的线程安全


1.不可变


在Java语言中,不可变(Immutable)的对象一定是线程安全的,无论是对象的方法实现还是方法的调用者,都不需要再采取任何的线程安全保障

措施,在第12章我们谈到final关键字带来的可见性时曾经提到过这一点,只要一个不可变的对象被正确地构建出来(没有发生this引用逃逸的情况),

那其外部的可见状态永远也不会改变,永远也不会看到它在多个线程之中处于不一致的状态。


2.绝对线程安全


绝对的线程安全完全满足Brian Goetz给出的线程安全的定义,这个定义其实是很严格的,一个类要达到“不管运行时环境如何,调用者都不需要任何

额外的同步措施”通常需要付出很大的,甚至有时候是不切实际的代价。在Java API中标注自己是线程安全的类,大多数都不是绝对的线程安全。我们

可以通过Java API中一个不是“绝对线程安全”的线程安全类来看看这里的“绝对”是什么意思。


3.相对线程安全


相对的线程安全就是我们通常意义上所讲的线程安全,它需要保证对这个对象单独的操作是线程安全的,我们在调用的时候不需要做额外的保障措施,

但是对于一些特定顺序的连续调用,就可能需要在调用端使用额外的同步手段来保证调用的正确性。


4.线程兼容


线程兼容是指对象本身并不是线程安全的,但是可以通过在调用端正确地使用同步手段来保证对象在并发环境中可以安全地使用,我们平常说一个类

不是线程安全的,绝大多数时候指的是这一种情况。


5.线程对立


线程对立是指无论调用端是否采取了同步措施,都无法在多线程环境中并发使用的代码。由于Java语言天生就具备多线程特性,线程对立这种排斥多

线程的代码是很少出现的,而且通常都是有害的,应当尽量避免。


线程安全的实现方法


1.互斥同步


互斥同步(Mutual Exclusion & Synchronization)是常见的一种并发正确性保障手段。同步是指在多个线程并发访问共享数据时,保证共享数据在

同一个时刻只被一个(或者是一些,使用信号量的时候)线程使用。而互斥是实现同步的一种手段,临界区(Critical Section)、互斥量(Mutex)

和信号量(Semaphore)都是主要的互斥实现方式。因此,在这4个字里面,互斥是因,同步是果;互斥是方法,同步是目的。


2.非阻塞同步


互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能问题,因此这种同步也称为阻塞同步(Blocking Synchronization)。从处理问题的方式

上说,互斥同步属于一种悲观的并发策略,总是认为只要不去做正确的同步措施,那就肯定会出现问题,无论共享数据是否真的会出现竞争,它都要

进行加锁、用户态核心态转换、维护锁计数器和检查是否有被阻塞的线程需要唤醒等操作。随着硬件指令集的发展,我们有了另外一个选择:基于

冲突检测的乐观并发策略,通俗地说,就是先进行操作,如果没有其他线程争用共享数据,那操作就成功了;如果共享数据有争用,产生了冲突,那

就才采取其他的补偿措施,这种乐观的并发策略的许多实现都不需要把线程挂起,因此这种同步操作称为非阻塞同步(Non-Blocking Synchronization)。


3.无同步方案


可重入代码(Reentrant  Code):这种代码也叫做纯代码(Pure Code),可以在代码执行的任何时刻中断它,转而去执行另外一段代码(

包括递归调用它本身),而在控制区返回后,原来的程序不会出现任何错误。相对线程安全来说,可重入性是更基本的特性,他可以保证线程安全,即所

有的可重入的代码都是线程安全的,但是并非所有的线程安全的代码都是可重入的。

线程本地存储(Thread Local Storage):如果一段代码中所需要的数据必须与其他代码共享,那就看看这些共享数据的代码是否能保证在同一个线程

中执行?如果能保证,我们就可以把共享数据的可见范围限制在同一个线程之内,这样,无须同步也能保证线程之间不出现数据争用的问题。


0 0
原创粉丝点击