java 线程安全的实现方式
线程安全的定义:
<<Java Concurrency In Practice>>的作者Brian Goetz 对"线程安全"有一段定义: 当多个线程访问一个对象的时候, 如果不用考虑这些线程在运行时环境下的调度和交替执行, 也不需要惊醒额外的同步,或者在调用方惊醒任何其他的协调操作,调用这个对象的行为都可以获取正确的结果,那这个对象就是线程安全的.
我们把java中各种操作共享的数据分成一下5类:
不可变, 绝对线程安全, 相对线程安全, 线程兼容, 线程对立.
线程安全的实现方法:
(1) 互斥同步:
互斥是因, 同步是果,推荐使用 synchronized 关键字进行同步, 在 concurrent包中有ReentrantLock类, 实现效果差不多. 还是推荐原生态的synchronized.
(2) 非阻塞同步:
需要硬件指令完成.常用的指令有:
Test-and-Set
Fetch-and-Increment
Swap
Compare-and-Swap (CAS)
Load-Linked/Store-Conditional (LL/SC)
典型的应用在 AtomicInteger 中
(3) 无同步方案
可重入代码: 在代码执行的任何时刻中断它, 转而去执行另外一段代码,而在控制权返回后,原来的程序不会出现任何错误.
可重入代码有一些共同的特性: 例如不以来存储在堆上的数据和公用的系统资源,用到的状态量都由参数中传入,不调用非可重入的方法等.
线程本地存储: 如果一段代码中所需要的数据必须与其他代码共享,那就看看这些共享数据的代码是否能保证在统一线程中执行?如果能保证,可以把共享数据的可见范围限制在同一个线程中,这样,无需同步也能保证线程之间不出现数据争用问题. 在java中可以通过使用 java.lang.ThreadLocal 开实现线程本地存储的功能.
这种应用在人人网的开源框架 Rose 有广泛的应用,最经典的应用在获取当前登录用户对象的时候, 是通过t票 到cache中来拿到当前登录用户的user对象. 对象就保存在 ThreadLocal 中. 经典的web交互模型中"一个请求对应一个服务器线程"的处理方式, 把user对象保存在线程本地存储中 可以很好的解决线程安全的问题.