Juc

Posted by lily's blog on October 18, 2024

Java多线程

多线程的状态

java中锁的分类

乐观锁悲观锁

读写锁

可重入锁

自旋锁

因此自旋锁适用于锁竞争不激烈、锁持有时间短的场景。

优缺点

CAS

是什么

怎么实现的

缺点

ABA问题

只能保证一个共享变量的原子操作

cpu开销大

基于CAS实现的类

原子类操作基本数据类型

AQS

CLH

CLH详解

CLH比传统CAS自旋锁优化点

  1. CLH数据结构
    1. 避免了饥饿问题
  2. 自旋获取锁时CLH前后节点状态关系
    1. 去中心化设计,高速缓存只受前一个节点影响
    2. 高竞争状态下cas不断争抢自旋,导致cpu的消耗
CLH 锁被认为是对自旋锁的一种改进,主要有以下几个原因:

1. **避免饥饿问题**:CLH 锁通过将线程组织成一个队列,确保先请求锁的线程优先获得锁。这种队列结构避免了传统自旋锁中可能出现的饥饿现象,即某些线程可能长时间得不到锁。

2. **降低 CPU 开销**:在 CLH 锁中,未获取锁的线程只需自旋检查前一个节点的状态,而不是检查全局状态。这种去中心化的设计使得锁的释放和获取对其他线程的影响范围更小,从而减少了 CPU 缓存失效的发生,降低了性能开销。

3. **减少竞争**:由于每个线程自旋在自己的状态变量上,而不是在一个共享的全局变量上,减少了锁竞争的情况。这样可以提高多线程环境下的并发性能。

4. **原子操作**:CLH 锁使用原子操作(如 `getAndSet`)来更新队尾指针,这保证了线程安全,避免了数据竞争。

虽然 CLH 锁中未获取锁的线程仍然需要自旋询问上一个线程的状态,但由于它们只关注前一个节点的状态,而不是全局状态,因此在高并发场景下,CLH 锁的性能通常优于传统的自旋锁。
  1. 锁的状态存储:在 CLH 锁中,每个线程都有一个独立的节点来表示其锁的状态。这些节点通常包含一个标志位,用于指示锁是否被持有。每个线程只需要关注其前一个节点的状态,而不是全局的锁状态。

  2. 自旋检查:当一个线程要获取锁时,它会自旋检查前一个节点的状态。如果前一个节点的状态表明锁已释放,当前线程就可以获取锁。这个过程只涉及到前一个节点的状态变量。

  3. 缓存失效:当一个线程释放锁时,它会更新自己的节点状态。由于其他线程在自旋时只关注前一个节点的状态,这意味着只有与当前线程相邻的那个后续线程会受到影响。具体来说,后续线程的高速缓存中存储的前一个节点的状态可能会变得无效,因为它需要重新读取这个状态。

  4. 减少竞争:与传统的自旋锁不同,传统自旋锁的状态是集中式的,所有线程都需要频繁地检查同一个锁状态变量。这种方式可能导致大量的缓存一致性流量,从而增加 CPU 的开销。而 CLH 锁通过去中心化的方式,减少了这种竞争,因为每个线程只关心自己的前一个节点的状态,只有在锁释放时才会影响到后续线程的缓存。

    synchronize

ThreadLocal

ReentrenLock