Semaphore原理

源码解析

Semaphore(信号量),这个类是用来控制并发时线程的数量的,首先这个类是实现了序列化接口
public class Semaphore implements Serializable(序列化)

我们主要来看看内部的一个结构和主要的方法acquire(获得)和release(释放)方法

其中这个类中有几个内部类,首先是抽象类 Sync 继承了同步器,

 abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 1192457210091910933L;
        //构造方法,设置了同步状态的值
        Sync(int permits) {
            setState(permits);
        }
        //返回当前的同步状态的值
        final int getPermits() {
            return getState();
        }
        //尝试获取不公平的锁
        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                //得到的同步状态的值
                int available = getState();
                //根据得到的值尝试修改同步状态的值
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }
        //尝试释放锁
        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;
            }
        }
        //减少同步状态值
        final void reducePermits(int reductions) {
            for (;;) {
                int current = getState();
                int next = current - reductions;
                if (next > current) // underflow
                    throw new Error("Permit count underflow");
                if (compareAndSetState(current, next))
                    return;
            }
        }
       //返回剩余的同步状态值
        final int drainPermits() {
            for (;;) {
                int current = getState();
                if (current == 0 || compareAndSetState(current, 0))
                    return current;
            }
        }
    }

然后分别有两个静态的内部类 NonfairSync(不公平模式)和FairSync(公平模式)实现了抽象类Sync,可以看到分别重写了同步器中的tryAcquireShared方法,下面是源码

static final class NonfairSync extends Sync {
        private static final long serialVersionUID = -2694183684443567898L;
        //调用了父类的构造函数
        NonfairSync(int permits) {
            super(permits);
        }
       //尝试获取锁,实际调用父类的nonfairTryAcquireShared方法
        protected int tryAcquireShared(int acquires) {
            return nonfairTryAcquireShared(acquires);
        }
    }

    /**
     * Fair version
     */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = 2014338818796000944L;
       //调用了父类的构造方法
        FairSync(int permits) {
            super(permits);
        }
        //可以看到和父类的方法中,只是多了一个hasQueuedPredecessors方法来先判断当前的
       // 对列中是否还有任务
        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;
            }
        }

接下来是构造方法

    //默认创建一个同步状态值为permits的不公平模式
    public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }
    //根据boolean判断生成不公平还是公平的模式
    public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }

然后是acquire() 方法

    //设置每个线程每个线程需要占用的同步状态值 默认为1
    public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
    //自定义每个线程每个线程需要占用的同步状态值
    public void acquire(int permits) throws InterruptedException {
        //必须大于0,否则抛出异常
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireSharedInterruptibly(permits);
    }

然后是release方法

   //释放当前线程所占用的同步状态值,默认为1
    public void release() {
        sync.releaseShared(1);
    }
 //释放当前线程所占用的同步状态值
    public void release(int permits) {
        //必须大于0 否则抛出异常
        if (permits < 0) throw new IllegalArgumentException();
        sync.releaseShared(permits);
    }

下面是一个Semaphore使用的示例


public class Demo extends Thread {
    private  Semap semap;
    public Demo(Semap semap){
        this.semap=semap;
    }
    @Override
    public void run(){
          semap.Test();
    }
}

public class Semap {
        //设置一个同步值为3的Semaphore
        Semaphore semaphore=new Semaphore(3);
        public void Test(){
            try {
                //设置每个线程要占用的同步值 默认为1
                semaphore.acquire();
                System.out.println(Thread.currentThread().getName() + " 开始: " + LocalTime.now());
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName() + " 结束: " + LocalTime.now());
                //释放所占用的状态值
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
}
//运行
public class SemaphoreDemo{
    public static void main(String[] args) {
        Semap semap=new Semap();
        for(int i=0;i<5;i++){
            new Demo(semap).start();
        }
    }
}
//运行结果
Thread-0 开始: 15:35:38.643
Thread-1 开始: 15:35:38.643
Thread-2 开始: 15:35:38.643
Thread-0 结束: 15:35:39.643
Thread-1 结束: 15:35:39.643
Thread-2 结束: 15:35:39.643
Thread-4 开始: 15:35:39.643
Thread-3 开始: 15:35:39.643
Thread-3 结束: 15:35:40.644
Thread-4 结束: 15:35:40.644

总结

可以看出来其实Semaphore内部其实是根据同步状态的值来限制并发的时线程的数量,当同步状态值为0时,后来的线程将被阻塞,直到有线程释放。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容