博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java并发包中Semaphore使用及源码分析
阅读量:4148 次
发布时间:2019-05-25

本文共 5788 字,大约阅读时间需要 19 分钟。

Semaphore是一个线程控制器,初始化时,给定一个线程数量,用来控制同一时刻,只能有指定数量的线程在执行,如果有其他线程,只能等待其中的线程释放掉之后,才可以继续执行。

给出一个简单的使用例子

public class SemaphoreDemo {    public final static int SEM_SIZE = 10;    public static void main(String[] args) {        Semaphore semaphore = new Semaphore(SEM_SIZE);        MyThread t1 = new MyThread("t1", semaphore);        MyThread t2 = new MyThread("t2", semaphore);        t1.start();        t2.start();        int permits = 5;        System.out.println(Thread.currentThread().getName() + " trying to acquire");        try {            semaphore.acquire(permits);            System.out.println(Thread.currentThread().getName() + " acquire successfully");            Thread.sleep(1000);        } catch (InterruptedException e) {            e.printStackTrace();        } finally {            semaphore.release();            System.out.println(Thread.currentThread().getName() + " release successfully");        }    }}class MyThread extends Thread {    private Semaphore semaphore;    public MyThread(String name, Semaphore semaphore) {        super(name);        this.semaphore = semaphore;    }    public void run() {        int count = 3;        System.out.println(Thread.currentThread().getName() + " trying to acquire");        try {            semaphore.acquire(count);            System.out.println(Thread.currentThread().getName() + " acquire successfully");            Thread.sleep(1000);        } catch (InterruptedException e) {            e.printStackTrace();        } finally {            semaphore.release(count);            System.out.println(Thread.currentThread().getName() + " release successfully");        }    }}

执行结果

main trying to acquiremain acquire successfullyt2 trying to acquiret2 acquire successfullyt1 trying to acquiremain release successfullyt1 acquire successfullyt2 release successfullyt1 release successfully

main线程先去尝试获取,传入进去的参数是5,这时候,state的值由最初设定的10,变为5,接下来,t2线程尝试去获取,将state的值降低减到2,当t1线程尝试去获取时,发现state的值,是小于acquire的值,获取失败,进行挂起,当main线程释放之后,t1线程就可以继续执行。

Semaphore的源码分析

Semaphore的内部结构是

Sync, 分别由NonfairSync 和 FairSync实现,整体来说和ReentrantLock比较类似,不同的是,sync方法当中实现的是AQS中的tryAcquireShared,对共享锁的获取方式和tryReleaseShared,

当线程中调用acquire方法的执行代码逻辑

public void acquire() throws InterruptedException {        sync.acquireSharedInterruptibly(1);    }

在AQS类中

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

默认的非公平的情况下,会判断当前state值和acquire,返回剩余的state值

final int nonfairTryAcquireShared(int acquires) {            for (;;) {                int available = getState();                int remaining = available - acquires;                if (remaining < 0 ||                    compareAndSetState(available, remaining))                    return remaining;            }        }

如果小于0,doAcquireSharedInterruptibly方法

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); // 如果是头节点,判断state的值是否大于0                    if (r >= 0) {                        setHeadAndPropagate(node, r); //如果state值大于0,将当前节点设为head节点                        p.next = null; // help GC                        failed = false;                        return;                    }                }                if (shouldParkAfterFailedAcquire(p, node) && //如果state的值小于0,挂起节点状态位SIGNAL的后一个节点                    parkAndCheckInterrupt())                    throw new InterruptedException();            }        } finally {            if (failed)                cancelAcquire(node);        }    }

关于release方法

public void release() {        sync.releaseShared(1);    }
public final boolean releaseShared(int arg) {        if (tryReleaseShared(arg)) {            doReleaseShared();            return true;        }        return false;    }
protected final boolean tryReleaseShared(int releases) {    for (;;) {        int current = getState();        int next = current + releases;        if (next < current) // overflow            throw new Error("Maximum permit count exceeded");        if (compareAndSetState(current, next))            return true;    }}

释放时,将state的值,在原来基础上加1,如果更新成功返回true,接下来执行释放操作

private void doReleaseShared() {              for (;;) {            Node h = head;            if (h != null && h != tail) {                int ws = h.waitStatus; //获取到头节点的状态                if (ws == Node.SIGNAL) {                      if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) //将头结点状态更新为0                        continue;            // loop to recheck cases                    unparkSuccessor(h);   //唤醒头结点的下个节点                }                else if (ws == 0 &&                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))                    continue;                // loop on failed CAS            }            if (h == head)                   // loop if head changed                break;        }    }
private void unparkSuccessor(Node node) {               int ws = node.waitStatus;        if (ws < 0)            compareAndSetWaitStatus(node, ws, 0);        Node s = node.next;        if (s == null || s.waitStatus > 0) {            s = null;            for (Node t = tail; t != null && t != node; t = t.prev)                if (t.waitStatus <= 0)                    s = t;        }        if (s != null)            LockSupport.unpark(s.thread);    }

唤醒头结点的后一个节点

以上分析,可以看出Semaphore和CountDownLatch的实现方式是很类似的。两者都是用的是同步队列,而CyclicBarrier使用的是AQS中的条件队列。

如有问题欢迎指正~

转载地址:http://zlvti.baihongyu.com/

你可能感兴趣的文章
Android进阶:Android开发究竟该如何学习,一线互联网公司面经总结
查看>>
Android进阶:你有过迷茫吗?使用指南
查看>>
android适配器,Android多线程实现方式及并发与同步,隔壁都馋哭了
查看>>
android适配布局,大佬手把手教你如何仿写出大厂的APP,一篇文章帮你解答
查看>>
android适配问题,年末阿里百度等大厂技术面试题汇总,架构师必备技能
查看>>
Android面试中常问的MMAP到底是啥东东?看这一篇就够了!
查看>>
Android面试官:2021年最新Android面试精讲,附带学习经验
查看>>
Android面试必问!Android性能优化之启动优化实战篇!实战解析
查看>>
Android面试必问!为了跳槽强刷1000道Android真题,实战解析
查看>>
Android面试总结,9次Android面试经验总结,附大厂真题面经
查看>>
flutter框架,这些Android高级必会知识点你能答出来几个?积累总结
查看>>
hashmap和hashtable的区别,腾讯T3团队整理,好文推荐
查看>>
hashmap面试题,面试字节跳动的Android工程师该怎么准备?面试必备
查看>>
jetpack库,关于网络优化你必须要知道的重点,成功拿下大厂offer
查看>>
kotlin入门,Android快速转战Kotlin教程,重难点整理
查看>>
kotlin入门,腾讯Android开发面试记录,面试必问
查看>>
kotlin教程!Android性能优化总结,论程序员成长的正确姿势
查看>>
mmkv原理,Android插件化主流框架和实现原理,再不刷题就晚了!
查看>>
mmkv原理,Android插件化主流框架和实现原理,再不刷题就晚了!
查看>>
mmkv缺点,腾讯3轮面试都问了Android事件分发,最强技术实现
查看>>