如何使用AQS共享锁,Semaphore、CountDownLatch

发布时间:2021-10-23 17:28:13 作者:iii
来源:亿速云 阅读:169

这篇文章主要讲解了“如何使用AQS共享锁,Semaphore、CountDownLatch”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何使用AQS共享锁,Semaphore、CountDownLatch”吧!

共享锁 和 AQS

1. 基于 AQS 实现的锁有哪些?

如何使用AQS共享锁,Semaphore、CountDownLatch  
图 18-1 基于 AQS 实现的锁

AQS(AbstractQueuedSynchronizer),是 Java 并发包中非常重要的一个类,大部分锁的实现也是基于 AQS 实现的,包括:

这一章节我们主要来介绍 Semaphore ,信号量锁的实现,其实也就是介绍一个关于共享锁的使用和源码分析。

2. Semaphore 共享锁使用

Semaphore semaphore = new Semaphore(2, false); // 构造函数入参,permits:信号量、fair:公平锁/非公平锁
for (int i = 0; i < 8; i++) {
    new Thread(() -> {
        try {
            semaphore.acquire();
            System.out.println(Thread.currentThread().getName() + "蹲坑");
            Thread.sleep(1000L);
        } catch (InterruptedException ignore) {
        } finally {
            semaphore.release();
        }
    }, "蹲坑编号:" + i).start();
}
 

这里我们模拟了一个在高速服务区,厕所排队蹲坑的场景。由于坑位有限,为了避免造成拥挤和踩踏,保安人员在门口拦着,感觉差不多,一次释放两个进去,一直到都释放。你也可以想成早上坐地铁上班,或者旺季去公园,都是一批一批的放行

「测试结果」

蹲坑编号:0蹲坑
蹲坑编号:1蹲坑

蹲坑编号:2蹲坑
蹲坑编号:3蹲坑

蹲坑编号:4蹲坑
蹲坑编号:5蹲坑

蹲坑编号:6蹲坑
蹲坑编号:7蹲坑

Process finished with exit code 0
 

3. Semaphore 源码分析

3.1 构造函数
public Semaphore(int permits) {
    sync = new NonfairSync(permits);
}

public Semaphore(int permits, boolean fair) {
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
 

permits:n. 许可证,特许证(尤指限期的)

默认情况下只需要传入 permits 许可证数量即可,也就是一次允许放行几个线程。构造函数会创建非公平锁。如果你需要使用 Semaphore 共享锁中的公平锁,那么可以传入第二个构造函数的参数 fair = false/true。true:FairSync,公平锁。在我们前面的章节已经介绍了公平锁相关内容和实现,以及CLH、MCS 《公平锁介绍》

「初始许可证数量」

FairSync/NonfairSync(int permits) {
    super(permits);
}

Sync(int permits) {
    setState(permits);
}

protected final void setState(int newState) {
    state = newState;
}
 

在构造函数初始化的时候,无论是公平锁还是非公平锁,都会设置 AQS 中 state 数量值。这个值也就是为了下文中可以获取的信号量扣减和增加的值。 

3.2 acquire 获取信号量
方法描述
semaphore.acquire()一次获取一个信号量,响应中断
semaphore.acquire(2)一次获取n个信号量,响应中断(一次占2个坑)
semaphore.acquireUninterruptibly()一次获取一个信号量,不响应中断
semaphore.acquireUninterruptibly(2)一次获取n个信号量,不响应中断
3.3 acquire 释放信号量
方法描述
semaphore.release()一次释放一个信号量
semaphore.release(2)一次获取n个信号量

有获取就得有释放,获取了几个信号量就要释放几个信号量。当然你可以尝试一下,获取信号量 semaphore.acquire(2) 两个,释放信号量 semaphore.release(1),看看运行效果 

3.4 公平锁实现

「信号量获取过程」,一直到公平锁实现。semaphore.acquire -> sync.acquireSharedInterruptibly(permits) -> tryAcquireShared(arg)

semaphore.acquire(1);

public void acquire(int permits) throws InterruptedException {
    if (permits < 0) throw new IllegalArgumentException();
    sync.acquireSharedInterruptibly(permits);
}

public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}

「FairSync.tryAcquireShared」

protected int tryAcquireShared(int acquires) {
    for (;;) {
        if (hasQueuedPredecessors())
            return -1;
        int available = getState();
        int remaining = available - acquires;
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}
 
3.5 非公平锁实现

「NonfairSync.nonfairTryAcquireShared」

protected int tryAcquireShared(int acquires) {
    return nonfairTryAcquireShared(acquires);
}

final int nonfairTryAcquireShared(int acquires) {
    for (;;) {
        int available = getState();
        int remaining = available - acquires;
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}
  
3.6 获取信号量失败,加入同步等待队列

在公平锁和非公平锁的实现中,我们已经看到正常获取信号量的逻辑。那么如果此时不能正常获取信号量呢?其实这部分线程就需要加入到同步队列。

「doAcquireSharedInterruptibly」

public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}

private void doAcquireSharedInterruptibly(int arg)
    throws InterruptedException {
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
 

4. CountDownLatch 共享锁使用

CountDownLatch 也是共享锁的一种类型,之所以在这里体现下,是因为它和 Semaphore 共享锁,既相似有不同。

CountDownLatch 更多体现的组团一波的思想,同样是控制人数,但是需要够一窝。比如:我们说过的4个人一起上皮划艇、两个人一起上跷跷板、2个人一起蹲坑我没见过,这样的方式就是门闩 CountDownLatch 锁的思想。

public static void main(String[] args) throws InterruptedException {
    CountDownLatch latch = new CountDownLatch(10);
    ExecutorService exec = Executors.newFixedThreadPool(10);
    for (int i = 0; i < 10; i++) {
        exec.execute(() -> {
            try {
                int millis = new Random().nextInt(10000);
                System.out.println("等待游客上船,耗时:" + millis + "(millis)");
                Thread.sleep(millis);
            } catch (Exception ignore) {
            } finally {
                latch.countDown(); // 完事一个扣减一个名额
            }
        });
    }
    // 等待游客
    latch.await();
    System.out.println("船长急躁了,开船!");
    // 关闭线程池
    exec.shutdown();
}
 

「测试结果」

等待游客上船,耗时:6689(millis)
等待游客上船,耗时:2303(millis)
等待游客上船,耗时:8208(millis)
等待游客上船,耗时:435(millis)
等待游客上船,耗时:9489(millis)
等待游客上船,耗时:4937(millis)
等待游客上船,耗时:2771(millis)
等待游客上船,耗时:4823(millis)
等待游客上船,耗时:1989(millis)
等待游客上船,耗时:8506(millis)
船长急躁了,开船!

Process finished with exit code 0
  

感谢各位的阅读,以上就是“如何使用AQS共享锁,Semaphore、CountDownLatch”的内容了,经过本文的学习后,相信大家对如何使用AQS共享锁,Semaphore、CountDownLatch这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!

推荐阅读:
  1. Join,CountDownLatch,CyclicBarrier,Semaphore和Exchanger
  2. Java并发包的CountDownLatch、CyclicBarrier、Semaphore

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

countdownlatch semaphore aqs

上一篇:Linux中比较有趣的命令行工具有哪些

下一篇:linux中less、Antiword和odt2xt程序怎么用

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》