线程安全的实现方法
== 同步 ==
同步是指当多个线程并发访问共享数据时,保证共享数据在同一时刻只能由一条线程使用。
阻塞【互斥同步】
-
Lock类【乐观锁】
-
ReentrantLock【重入锁】
ReentrantLock 类内部总共存在三个类,分别为Sync、NonfairSync、FairSync三个类,NonfairSync与 FairSync类继承自 Sync类,Sync类继承自AbstractQueuedSynchronizer【AQS】抽象类
ReentrantLock() 型构造函数,默认是采用的非公平策略获取锁;
public ReentrantLock() {
// 默认非公平策略
sync = new NonfairSync();
}ReentrantLock(boolean)型构造函数,可传参确定采用公平策略或者是非公平策略,参数为true表示公平策略,否则采用非公平策略:
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}-
Sync
-
NonfairSync【非公平策略:hasQueuedPredecessors=false】
NonfairSync类实现了Sync类中抽象的 lock方法,方法中,当前资源每一次都尝试获取锁,并不会按照公平等待的原则进行等待,让等待时间最久的线程获得锁。
- 优势:公平平等,在等待一段时间后总有执行的机会
- 劣势:更慢,吞吐量更小
-
FairSync【公平策略:hasQueuedPredecessors=true】
FairSync类,当资源空闲时,会先判断sync队列(AQS中的数据结构)是否有等待时间更长的线程,如果存在,则将该线程加入到等待队列的尾部,实现了公平获取原则
- 优势:更快,吞吐量更大
- 劣势:可能产生县城饥饿,即某些线程在长时间内,始终得不到执行
-
-
ReentrantReadWriteLock【读写锁】
一种支持一写多读的同步锁,读写分离,可分别分配读和写锁,支持多次分配读锁,使多个操作可以并发执行。
-
-
synchronized关键字【悲观锁】
JVM在执monitorenter指令前,首先尝试获取对象的锁。
如果这个对象没被锁定,或者当前线程已经拥有该对象的锁,那么就将锁的计数器的值+1。当执行monitorexit指定时就将锁计数器的值减1。一旦计数器的值为0,那么锁随机就被释放了。
如果当前线程不能获取锁对象,那么当前线程会阻塞等待直到请求的对象被所持有的线程释放。
非阻塞【非阻塞同步】
- CAS(compare and swap)
== 无同步 ==
Reentrant Code【可重入代码】
ThreadLocal【线程变量】
ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量。
ThreadLocal是作为当前线程中,属性ThreadLocalMap集合中的某一个Entry的key值Entry(threadlocl,value),虽然不同的线程之间threadlocal这个key值是一样,但是不同的线程所拥有的ThreadLocalMap是独一无二的,也就是不同的线程间同一个ThreadLocal(key)对应存储的值(value)不一样,从而到达了线程间变量隔离的目的,但是在同一个线程中这个value变量地址是一样的。
-
应用场景
-
存储用户Session
-
数据库连接,处理数据库事务
动态数据源切库
-
数据跨层传递(controller,service, dao)
比如说我们是一个用户系统,那么当一个请求进来的时候,一个线程会负责执行这个请求,然后这个请求就会依次调用service-1()、service-2()、service-3()、service-4(),这4个方法可能是分布在不同的类中的。都可以拿到用户信息
-
Spring使用ThreadLocal解决线程安全问题
一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。
一般的Web应用划分为展现层、服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。
线程安全问题都是由全局变量及静态变量引起的
-
XMind - Trial Version