Java JUC包源码分析 - 信号量Semaphore

信号量是基于共享锁和许可的原理实现,在初始时给定许可数,获取时如果当前许可足够,就获得许可往下执行,否则就阻塞。

信号量还分公平信号量和非公平信号量,两者的区别就在于再尝试获取许可的时候,公平信号量还需要判断当前线程所在的节点是不是CLH队列的头节点,其他都一样。释放锁的过程两者也是一样的。

先看下用法:

package com.pzx.test.test00001;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Semaphore;

public class SemaphoreDemo {
    static Semaphore semaphore = new Semaphore(10);
    static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ");

    public static void main(String[] args) {
        ThreadA threadA = new ThreadA();
        for (int i = 1; i <= 4; i++) {
            Thread thread = new Thread(threadA, "t"+i);
            threadA.setPermits(3);
            thread.start();
        }
    }

    static class ThreadA implements Runnable {
        int permits;

        @Override
        public void run() {
            int p = permits;
            try {
                semaphore.acquire(p);
                System.out.println(sdf.format(new Date()) + Thread.currentThread().getName() + " acquired permit, available permits:" + semaphore.availablePermits() + " at now");
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                semaphore.release(p);
                System.out.println(sdf.format(new Date()) + Thread.currentThread().getName() + " released permit, available permits:" + semaphore.availablePermits() + " at now");
            }
        }

        public int getPermits() {
            return permits;
        }

        public void setPermits(int permits) {
            this.permits = permits;
        }
    }
}

输出: 

2018-09-11 22:21:44 t3 acquired permit, available permits:1 at now
2018-09-11 22:21:44 t1 acquired permit, available permits:1 at now
2018-09-11 22:21:44 t2 acquired permit, available permits:1 at now   //这里隔了5秒才往下执行,线程t4等了5秒才获取许可
2018-09-11 22:21:49 t3 released permit, available permits:7 at now
2018-09-11 22:21:49 t4 acquired permit, available permits:7 at now
2018-09-11 22:21:49 t1 released permit, available permits:7 at now
2018-09-11 22:21:49 t2 released permit, available permits:7 at now
2018-09-11 22:21:54 t4 released permit, available permits:10 at now

再看源码

主要就看下构造函数,acquire和release方法

// 抽象内部类Sync,又有两个公平与非公平的子类
public class Semaphore implements java.io.Serializable {
    private static final long serialVersionUID = -3222578661600680210L;
    /** All mechanics via AbstractQueuedSynchronizer subclass */
    private final Sync sync;

    // 继承AQS
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 1192457210091910933L;
        // 构造函数就是设置许可数为state数
        Sync(int permits) {
            setState(permits);
        }

        final int getPermits() {
            return getState();
        }

        // 非公平的获取锁
        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                // 计算剩余的许可数
                int remaining = available - acquires;
                // 如果剩余的许可数<0,就返回这个负数
                // 如果剩余许可数>=0,就设置state为剩余的数,设置成功才返回剩余的许可数,
                // 否则重试
                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");
                // 如果设置成功就返回true,否则重试
                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;
            }
        }
        
        // 许可数置0
        final int drainPermits() {
            for (;;) {
                int current = getState();
                if (current == 0 || compareAndSetState(current, 0))
                    return current;
            }
        }
    }

    // 非公平版本的信号量
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = -2694183684443567898L;

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

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

    // 公平版本的信号量,就是再获取共享锁时判断下当前线程是不是再CLH队列头
    static final class FairSync extends Sync {
        private static final long serialVersionUID = 2014338818796000944L;

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

        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;
            }
        }
    }
    
    // 构造函数
    public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }
    public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }

    // 获取许可数
    public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
    public void acquire(int permits) throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireSharedInterruptibly(permits);
    }

    // AQS里面的代码
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        // 尝试获取共享锁,这是上面重写的方法,获取失败是返回负数
        if (tryAcquireShared(arg) < 0)
            // 实际去获取锁,如果没获取到就阻塞,等待其他许可释放的时候被唤醒
            doAcquireSharedInterruptibly(arg);
    }

    // 释放许可
    public void release() {
        sync.releaseShared(1);
    }
    public void release(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        sync.releaseShared(permits);
    }

    // AQS里面的释放共享锁
    public final boolean releaseShared(int arg) {
        // 尝试释放锁就再上面的Sync里有实现,尝试成功后就
        if (tryReleaseShared(arg)) {
            // 以共享模式释放锁,其实里面的操作主要目的是唤醒后继线程,也就是还没有获取许可,
            // 阻塞在那里的线程
            doReleaseShared();
            return true;
        }
        return false;
    }

 

你可能感兴趣的:(Java源码分析,Java多线程)