ReentrantLock在Java中是通过AbstractQueuedSynchronizer(AQS)框架实现的,它提供了公平锁(FairSync)和非公平锁(NonfairSync)两种模式。这两种锁的实现主要区别在于获取锁的策略。
import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockDemo { // 非公平锁 private static final ReentrantLock nonFairLock = new ReentrantLock(); // 公平锁 private static final ReentrantLock fairLock = new ReentrantLock(true); // 传入true参数表示使用公平锁 public static void main(String[] args) { //demonstrateLock(nonFairLock, "Non-Fair Lock"); demonstrateLock(fairLock, "Fair Lock"); } private static void demonstrateLock(ReentrantLock lock, String lockType) { class Task implements Runnable { @Override public void run() { for (int i = 0; i < 5; i++) { lock.lock(); try { System.out.println(Thread.currentThread().getName() + " acquired " + lockType + ", iteration: " + i); Thread.sleep(10); // 模拟持有锁时的工作 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { lock.unlock(); } } } } Thread t1 = new Thread(new Task(), "Thread-1"); Thread t2 = new Thread(new Task(), "Thread-2"); t1.start(); t2.start(); try { t1.join(); // 等待所有线程完成 t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } } }
公平锁(FairSync)的实现:
公平锁在尝试获取锁时,会首先检查队列中是否有其他等待的线程。如果有其他线程已经在等待队列中,那么新来的线程就会加入到队列的末尾排队等待,而不是尝试直接获取锁。这样可以确保线程按照它们请求锁的顺序来获取锁,体现了公平性。公平锁的实现步骤大致如下:
1. 调用`lock()`方法时,首先通过CAS操作尝试快速获取锁。
2. 如果失败,检查AQS的等待队列中是否已经有线程在等待。如果有,当前线程就加入到队列末尾等待,不会进行进一步的尝试。
3. 当前线程会进入阻塞状态,直到被AQS唤醒,通常是前一个持有锁的线程释放锁并唤醒它。
### 非公平锁(NonfairSync)的实现:
非公平锁在尝试获取锁时,不管队列中是否有其他线程在等待,总是会先尝试直接通过CAS操作快速获取锁,这可能导致新来的线程“插队”,获得锁的机会优于已经在队列中等待的线程。这种设计牺牲了公平性以换取更高的吞吐量。非公平锁的实现步骤大致如下:
1. 在`lock()`方法中,同样首先尝试通过CAS操作快速获取锁,即使此时有其他线程已经在队列中等待。
2. 如果快速获取失败,非公平锁仍然可能会继续尝试CAS获取锁,即使队列中已有等待线程。这意味着它可能在某些情况下直接获取锁,跳过队列中的其他线程。
3. 只有当多次尝试CAS获取锁都失败后,才会将当前线程加入到等待队列,并进入阻塞状态。
无论是公平锁还是非公平锁,它们都使用了AQS的内部FIFO队列来管理等待的线程,以及通过状态位(state)和等待节点(Node)来协调线程的阻塞与唤醒。此外,ReentrantLock还支持可重入特性,即已经持有锁的线程再次请求相同锁时可以直接获取,而不需要再次排队。
标签:Thread,队列,ReentrantLock,获取,线程,公平,底层 From: https://www.cnblogs.com/89564f/p/18186675