4.1 锁的优化有哪些点?
- 减少锁的持有时间:尽量避免对整个方法synchronized,只在必要时进行synchronized
- 减小锁粒度:即缩小锁定对象的范围。例如ConcurrentHashMap对其中的某个段加锁,不要对整个HashMap加锁
- 使用读写锁,即ReadWriteLock
- 锁分离:把读写锁的思想作延伸,对不同的操作功能加锁。例如LinkedBlockingQueue的take()和put()方法使用不同的锁
- 锁粗化:如果对同一个锁不断地进行请求,同步和释放,这些操作本身就会占用大量的系统资源
- JVM对锁的优化:
- 偏向锁:当一个线程获得锁,就进入偏向模式。再次请求锁时,无需同步
- 轻量级锁:如果偏向锁失败,则尝试轻量级锁。若加锁失败轻量级锁被其他线程争夺到,则转为重量级锁
- 自旋锁:在轻量级锁状态下继续锁竞争,没有抢到锁的线程将自旋,即不停地循环判断锁是否能够被成功获取
- 锁消除:去除不可能存在共享资源竞争的锁
4.3 ThreadLocal是与锁不同的另一个思路
除了锁可以保证线程安全,还可以通过增加资源来保证
ThreadLocal实际是Thread.ThreadLocalMap中一条记录的引用。而这个ThreadLocalMap实际上是在任何地方都可以访问到的。所以,
他实际是在存储全局变量,只不过这个全局变量比较特殊,每一个实例都跟一个线程绑定了。
你在A线程set了这个变量进去,那这个变量只是A的,只有A线程运行时才能访问到,线程B是访问不到的,除非线程B也set一个,也就是所谓的每个线程持有一个副本1
2
3
4
5
6
7
8//获取ThreadLocal的值
public T get() { }
//设置ThreadLocal的值
public void set(T value) { }
//删除ThreadLocal
public void remove() { }
//初始化ThreadLocal的值
protected T initialValue() { }这里仅了解,后续会作专栏深入分析
4.4 CAS比较交换也是与锁不同的一种新思路
- CAS(V,E,N)三个参数,V表示要更新的变量,E表示预期值,N表示新值。
- 只有V=E时,才会更新V为N。否则宣布失败,并返回V当前值,并且不会被挂起,可再次尝试
- 简单的说,E就是你以为V应该是多少了,如果V不是你想的那样,说明已经被其他线程改过了,就得重新读,再次尝试就好了
JDK并发包中的atomic包就是使用CAS理论:
- AtomicInteger:基于CAS理论的无锁的线程安全的整数,原理如图 incrementAndGet方法是使用CAS操作让自己+1