锁优化及注意事项
- 有助于提高锁的性能
- 减小所持有时间:例如不要对方法直接加锁,而是在方法中对具体访问临界资源的代码加锁
- 减小锁粒度:如ConcurrentHashMap
- 用读写锁代替独占锁
- 锁分离:如LinkedBlockingQueue,由于读写分别在队首和队尾,所以不对整个队加锁而是对队首和队尾分别加锁
- 锁粗化:当一个锁在某段时间对某一资源反复申请锁,可以现将锁分给此线程一段时间,阻塞其他线程
- Java虚拟机的锁优化
- 锁偏向:因为很有可能是同一个线程申请某个锁,所以如果一个线程获取了锁,那么就进入偏向模式,当此线程需要再次申请锁时,无需再进行同步操作。
- 轻量级锁:如果偏向锁实效,虚拟机不会立即挂起线程,而是会简单地将对象头部作为指针,指向持有锁的线程堆栈的内部,来判断一个线程是否持有对象锁,如果线程获得轻量级锁成功,则进入临界区,否则当前线程酒会膨胀为重量级锁
- 自旋锁:锁膨胀后,虚拟机为了避免线程在操作系统层面真正被挂起。系统会进行一次赌注,假设在不久的将来线程会获得锁,所有会让当前线程进行几个空循环,若经过几个时钟周期后获得锁,则进入临界区,否则真正挂起
- 锁消除:虚拟机在JIT编译时,对运行上下文进行扫描,去除不可能存在共享资源的竞争的锁。主要只是指JDK一些内置API,如StringBuffer、Vector。
- ThreadLocal:线程本地变量,为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
- synchronized是用时间保证安全,ThreadLocal是用空间保证安全
- 实现:
- ThreadLocal类有一个内部类ThreadLocalMap,用来保存键值对,当前ThreadLocal为键,要存储的副本为值。每一个Thread都有一个ThreadLocalMap类型成员threadLocals用来保存键值对
- 初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals
- 详细说明:http://www.cnblogs.com/dolphin0520/p/3920407.html
- 无锁:无锁是一种乐观的策略,他会假设对临界区资源的访问是没有冲突的。无锁策略使用一种叫做比较交换的技术(CAS--Compare and swap),一旦检测到冲突,就重试当前操作直到没有冲突为止。
- 无锁没有竞争带来的系统开销,也没有频繁调度的开销
- 算法:三个参数,V要更新的变量、E预期值、N新值。仅当V值等于E值时,才会将V值设为N值,否则认为其他线程做了更新。最后CAS返回当前V真实值。当多个线程同时使用CAS操作一个变量时,只有一个会胜出,其余均失败,失败的线程不会被挂起,而是再次尝试,也允许放弃。总之,CAS操作需要给出一个期望值,也就是你认为现在变量应该是什么样子,如果不如你所愿,说明变量已经被修改过了,你需要重新去读变量值,再修改。