第二章:线程安全性
2.1 什么是线程安全性
可以被多个线程调用,并且在线程之间不会出现错误的交互
方法内的局部变量不需要保护,因为它存储在栈中,是每个线程独有的
2.2 原子性
一个共享变量可以定义为原子变量:atomic
多个共享变量时,之间可能存在某种依赖关系,分别定义为原子变量会由于竞态条件,出现错误,比如先检查后执行。
竞态条件:某个计算的正确性取决于多个线程的交替执行时序时
这就需要复合操作,将多个变量的读写操作作为一个程序块,接下来采用
2.3 加锁机制
将一个复合程序块采用内置锁加锁。synchronized,把方法调用所在的对象作为一个锁,相当于一个互斥体,程序块只能被该对象进入,但是由于内置锁是可重入的,也就是程序块可以被该对象多次进入,常用于子类复写基类的synchronized方法时。
2.4 用锁来保护状态
不止可用内置锁,还可以用显示的锁来执行同步,且多个synchronized方法有相互依赖关系时,还需要其他的锁机制
2.5 活跃性与性能
由于上述方法会造成性能非常低,如果一个程序块用时较长,则后续线程会长时间等待,应该讲锁的粒度降低,但是在性能与程序简单性方面要取得平衡
第三章 对象的共享
3.1 可见性
线程A修改变量i,线程B是否可立即看到?
synchronized不仅可以用来加锁,还可以用来同步
非原子的64位操作:会被分解为两个32位操作,因此必须在多线程环境下申明共享且可变的long或者double类型的数据时,用volatile修饰,或者加锁保护。volatile用来将变量的更新同步到其他线程,只能用于可见性,不能用于原子性。在访问volatile变量时不会执行加锁操作,因此volatile是一种比synchronized更轻量级的同步机制。
volatile与加锁的选择:
volatile常用于简单的状态标志,但不保证递增的原子性,在1、对变量的写入不依赖当前值2、该变量不会与其他变量一起纳入不可变条件中3、在访问变量时不需要加锁的情况下,才使用。
加锁即可保证原子性,又可保证可见性。
3.2 对象的发布与逸出
当一个对象发布时,在该对象内引用的非私有对象一同被发布。
构造函数里的this指针很容易被逸出,可以用私有构造函数+实例工厂来避免
3.3 线程封闭
仅在单线程内访问数据,叫做线程封闭。比如JDBC的connection对象。从连接池获取一个connection对象,直到用完后返回,连接池不会将该对象分配给其他线程使用。
栈封闭:申明为一个方法中的局部变量
ThreadLocal类:用于防止对可变的单实例变量或全局变量进行共享。比如在一个进程中使用的一个connection对象,在其内部所有的方法都可使用,但不可被其他进程访问。
3.4 不变性
不可变变量:final
3.5 安全发布
多个线程间共享对象,必须确保安全的共享
可变对象必须通过安全的方式来发布,也就是必须使用同步。一个正确构造的对象可通过以下方式来发布:1、在静态初始化函数中初始化一个对象引用2、将对象的引用保存到volatile类型的域或者atomicReferance类型的对象中3、将对象的引用保存到某个正确构造对象的final域中4、将对象的引用保存到由一个锁保护的域中
原文地址:http://muyunzhe.blog.51cto.com/9164050/1719927