LockSupport.java 中的 FIFO 互斥代码片段

Posted

技术标签:

【中文标题】LockSupport.java 中的 FIFO 互斥代码片段【英文标题】:FIFO mutex code snippet in LockSupport.java 【发布时间】:2020-06-15 20:36:28 【问题描述】:

我试图了解LockSupport.java 并参考了它的文档。在那里,它有代码 sn-p,其中作者提供了使用 LockSupport#park() 和 LockSupport#Unpark() 实现互斥锁的示例。

我使用该示例并创建了一个使用该示例 FIFOMutex 的程序。

FIFOMutex 代码(取自 LockSupport.java)

package com.example.java.locking.studies.locksupport.from.javadoc;

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;

class FIFOMutex 
    private final AtomicBoolean locked = new AtomicBoolean(false);
    private final Queue<Thread> waiters = new ConcurrentLinkedQueue<Thread>();

    public void lock() 
        boolean wasInterrupted = false;
        Thread current = Thread.currentThread();
        waiters.add(current);

        // Block while not first in queue or cannot acquire lock
        while (waiters.peek() != current ||
                !locked.compareAndSet(false, true)) 
            LockSupport.park(this);
            if (Thread.interrupted()) // ignore interrupts while waiting
                wasInterrupted = true;
        

        waiters.remove();
        if (wasInterrupted) // reassert interrupt status on exit
            current.interrupt();
    

    public void unlock() 
        locked.set(false);
        LockSupport.unpark(waiters.peek());
    

现在,我编写了下面的程序来使用FIFOMutex作为锁定机制,代码如下:

package com.example.java.locking.studies.locksupport.from.javadoc;

public class FIFOMutexMainApp 

    public static void main(String[] args) throws InterruptedException 

        FIFOMutex mutex = new FIFOMutex();

        Thread t1 = new Thread(new Runnable() 
            @Override
            public void run() 
                System.out.println("Inside thread -> " + Thread.currentThread() + "Before mutex.lock()");
                mutex.lock();
                System.out.println("Inside thread -> " + Thread.currentThread() + "After mutex.lock()");

                for (;;) 

                
            
        );

        Thread t2 = new Thread(new Runnable() 
            @Override
            public void run() 
                System.out.println("Inside thread -> " + Thread.currentThread() + "Before mutex.lock()");

                mutex.unlock();
                System.out.println("Inside thread -> " + Thread.currentThread() + "After mutex.lock()");

                for (;;) 

                
            
        );

        t1.start();

        Thread.sleep(10);

        t2.start();

        Thread.sleep(100);

        mutex.lock();

        System.out.println("Inside thread -> " + Thread.currentThread() + "will I get printed?");
    

下面是这个程序的输出:

Inside thread -> Thread[Thread-0,5,main]Before mutex.lock()
Inside thread -> Thread[Thread-0,5,main]After mutex.lock()
Inside thread -> Thread[Thread-1,5,main]Before mutex.lock()
Inside thread -> Thread[Thread-1,5,main]After mutex.lock()
Inside thread -> Thread[main,5,main]will I get printed?

Thread-1 启动并获取锁,随后是调用 unlock() 的 Thread-2。主要调用 lock()。我的理解是 Main 将永远被阻塞,即使 Thread-2 调用了 unlock(),因为 Thread-2 从未调用 lock()(因此,它从未被授予 permit)。

我无法理解这种行为。如果我在 Thread-2 中注释 unlock(),那么主线程将永远被阻塞。

一个线程(在我的情况下是 Thread-2)怎么可能调用 unlock()(以前没有调用 lock())而其他线程(在这种情况下是主线程)调用 lock(),但从来没有被锁了?

【问题讨论】:

【参考方案1】:

如果你深入源码,unblock 方法实际上是释放变量locked 提供的 CAS 锁,并 unpark 第一个线程。所以如果注释掉unlock()的调用,CAS锁不会被释放,主线程可以永远停留在while循环中,从而阻塞主线程。

【讨论】:

【参考方案2】:

首先:FIFOMutex 调用

Thread current = Thread.currentThread();

意味着实际线程被用于验证。这意味着考虑了最实际的线程,即:

线程 1 首先发出锁,然后 线程 2 被调用并发出解锁。

这就是主线程不会挂起的原因。 但是:如果您评论线程 2 的解锁,则没有完成并且主线程挂起。这很好理解,不是吗?

【讨论】:

以上是关于LockSupport.java 中的 FIFO 互斥代码片段的主要内容,如果未能解决你的问题,请参考以下文章

跟踪FIFO队列中的最大元素[重复]

linux中的pipe和fifo的区别

FIFO管道中的数据丢失?

直径中的 FIFO / 队列的符号?

使用 2 个 FIFO 的客户端-服务器 IPC 消息中的 kill() 函数问题

命名管道FIFO