当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方法进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的。-----Brian Goetz
笔者理解:当一个线程访问一个对象期间,不用考虑因为其他线程的访问造成该线程获取到错误的结果,那这个对象是线程安全的。
周志明在书中将线程安全涉及到的数据分为五类:不可变、绝对线程安全、相对线程安全、线程兼容和线程对立。
1.不可变
如果一个对象的对外可见的状态是不可变的,那么这个对象一定是线程安全的。在java中主要用关键字final来实现不可变。
如果final修饰的是基本数据类型,那这个数据一定是不可变的;如果final修饰的是一个对象,当这个对象对外可见的状态是不可变得时,那它就是线程安全的,最典型的的对象参照String。
2.绝对线程安全
绝对线程安全就是本篇文章开头降到的线程安全。即,不管运行时环境如何,调用者都不需要任何额外的同步措施。
3.相对线程安全
相对线程安全是我们通常理解的线程安全。eg:Vector、HashTable、Collections的synchronizedCollection()方法包装的集合等。对于单个对象来说,调用时不用做额外的保护措施,但是涉及到一条线程调用对象时修改了对象的状态,从而导致另一条线程执行期间获取到的该对象的状态发生改变而引起的程序异常时,需要程序员针对程序进行编码保护,例如Vector在循环遍历时,另一条数据修改了对象的长度,从而导致抛出ArrayIndexOutOfBoundsExcetion。
4.线程兼容
对象本事不是线程安全的,但是可以通过编程保证对象是线程安全的。
5.线程对立
无论做了何种保护措施都无法保证线程安全的情况。
1.互斥同步
互斥同步是最常见的一种并发正确性保障的手段。原理是,一段指令或者一个对象在调用时,保证该对象同时只被一条线程调用,以此实现线程安全。最常见的是关键字sychronized,ReentrantLock。
2.非阻塞同步
当需要访问非线程安全的对象时,暂时不考虑线程安全问题,当操作完成后进行提交时,再去对比引用的对象与当前的对象状态是否一致,如果一致则说明这个操作期间对象是线程安全的,然后提交操作;如果不一致,在采取其他方式进行补偿(补偿的方式通常是重试)。例如,CAS操作。