4.1 锁的优化有哪些点?

  • 减少锁的持有时间:尽量避免对整个方法synchronized,只在必要时进行synchronized
  • 减小锁粒度:即缩小锁定对象的范围。例如ConcurrentHashMap对其中的某个段加锁,不要对整个HashMap加锁
  • 使用读写锁,即ReadWriteLock
  • 锁分离:把读写锁的思想作延伸,对不同的操作功能加锁。例如LinkedBlockingQueue的take()和put()方法使用不同的锁
  • 锁粗化:**如果对同一个锁不断地进行请求,同步和释放,这些操作本身就会占用大量的系统资源**

image.png

  • JVM对锁的优化:
    • 偏向锁:当一个线程获得锁,就进入偏向模式。再次请求锁时,无需同步
    • 轻量级锁:如果偏向锁失败,则尝试轻量级锁。若加锁失败轻量级锁被其他线程争夺到,则转为重量级锁
    • 自旋锁:在轻量级锁状态下继续锁竞争,没有抢到锁的线程将自旋,即不停地循环判断锁是否能够被成功获取
    • 锁消除:去除不可能存在共享资源竞争的锁

4.3 ThreadLocal是与锁不同的另一个思路

  • 除了锁可以保证线程安全,还可以通过增加资源来保证
  • ThreadLocal实际是Thread.ThreadLocalMap中一条记录的引用。而这个ThreadLocalMap实际上是在任何地方都可以访问到的。所以, **他实际是在存储全局变量,只不过这个全局变量比较特殊,每一个实例都跟一个线程绑定了**。 你在A线程set了这个变量进去,那这个变量只是A的,只有A线程运行时才能访问到,线程B是访问不到的,除非线程B也set一个,也就是所谓的每个线程持有一个副本

    1. //获取ThreadLocal的值
    2. public T get() { }
    3. //设置ThreadLocal的值
    4. public void set(T value) { }
    5. //删除ThreadLocal
    6. public void remove() { }
    7. //初始化ThreadLocal的值
    8. 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

image.png