【Java并发】线程安全
什么是线程安全?
当多个线程访问一个类时,如果不用考虑这些线程在运行时环境下的调度和交替执行,并且不需要额外的同步及在调用方代码不必作其他的协调,这个类的行为仍然是正确的,则称这个类是线程安全的类。
?
无状态的类是线程安全的
如果一个类是无状态的:不包含域也没有引用其他类的域。一次特定计算的瞬时状态会唯一地存在本地变量中,这些本地变量存储在线程的栈中,只有执行线程才能访问。
?
编写线程安全的代码,本质上就是管理对状态的访问,而且是共享的/可变的状态。
关于状态:一个对象的状态就是该对象的数据,存储在状态变量中,如实例域或静态域,还包括附属对象的域。如HashMap的状态一部分存储在对象本身中,同时也存储到很多Map.Entry对象中。
关于共享:一个变量可以被多个线程同时访问。
关于可变:变量的值在其生命周期内可以改变。
只要有多于一个的线程访问给定的状态变量,而且其中某个线程会写入该变量,此时必须使用同步来协调线程对该变量的访问。
Java中首要的同步机制是synchronized关键字,它提供了独占锁。另外,“同步”还包括volatile变量,显示锁和原子变量的使用。
面对一个有多线程访问隐患的程序,可以从以下三个方法来修复:
1.不要使用跨线程的共享变量;
2.使状态变量变为不可变的;
3.在任何访问状态变量的时候使用同步
注意一开始就将一个类设计成是线程安全的,比在后期重新修复更容易,并且并发访问共享状态变量带来的问题往往不好重现。另外,设计线程安全的类时,要充分利用优秀的面向对象技术——如封装。
?
处理并发程序常见的问题:
1.竞争条件 Race Condition
??? a.原子操作问题
自增,自减操作不是原子操作,是读-改-写复合操作,可以用线程安全的原子变量(在java.util.concurrent.atomic包中)来作为计数器。
??? b.检查再运行
例如:你观察到一些事情为真(文件x不存在),然后(then)基于你的观察去执行一些动作(创建文件x);事实上,从观察到执行操作的这段时间里,观察结果可能已经失效了(有人在此期间创建了文件x),从而引发错误(非预期的异常,重写数据或者破坏文件)。
再例如惰性初始化。
?
操作共享状态的复合操作必须是原子的,以避免上面的竞争条件,通过对该复合操作加锁可以确保其行为是原子的。
?
问题:只有在写入共享变量时才需要同步么?
3.1节
?
在java中有哪些保证线程安全的机制或者措施?
锁机制(implicit lock和explicit lock)参见 http://nemogu.iteye.com/blog/1409879