并发编程工具之三:Semaphore 用法及源码分析

来源:互联网 发布:label mx标签打印软件 编辑:程序博客网 时间:2024/05/24 04:17

1. 信号量Semaphore的介绍

信号量可以看做对锁的扩展,我们知道的synchronized 和 重入锁 ReentrantLock ,一次只允许一个县城进行临界区资源访问,而信号量可以使多个线程同时访问某一资源。

如在营业厅办理手机卡业务,只有有限个窗口来同时办理业务。如果所有窗口都在办理业务中,那么这时过来办理业务的人需要等待,直到有人办理完并通知下一位时,这个人才可以开始自己的业务的办理。在这期间,这个人需要一直等待。在这里负责通知下一位人员继续办理业务的系统(或者人)就起到了信号量(非负数)的作用,表示公共资源(窗口)的可用数量,当一个线程要使用公共资源时,需要查看信号量是否大于0,如果大于0,则将其减1,并取占用窗口。如果为0,线程会阻塞,知道有其他线程释放资源。

我们先来熟悉一下这个类的主要功能性方法:

public Semaphore(int permits){...} public Semaphore(int permits, boolean fair) {...} // 第二个参数指定公平与否public void acquire() throws InterruptedException {...} // 获取资源,获取不到会一直阻塞public void acquireUninterruptibly() {...} // 同acquire(),但不会响应中断public boolean tryAcquire() {...}  // 尝试获取资源,获取不到直接返回falsepublic boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException {...}                            // 等待指定时间来获取资源,获取不到返回falsepublic void release() {...} // 释放资源public void acquire(int permits) throws InterruptedException {...} // 获取指定数量的资源,获取不到会一直阻塞public void acquireUninterruptibly(int permits) {...} // 同acquire(int permits),但不会响应中断public boolean tryAcquire(int permits) {...}public boolean tryAcquire(int permits, long timeout, TimeUnit unit)                 throws InterruptedException {...}public void release(int permits) {...}public int availablePermits() {...} // 返回可使用资源数量public int drainPermits() {...}protected void reducePermits(int reduction) {...}public boolean isFair() {...}public final boolean hasQueuedThreads() {...}public final int getQueueLength() {...}protected Collection<Thread> getQueuedThreads() {...}public String toString() {...}

使用最多的是方法acquire() 和release() 。

2. acquire() 和release()源码分析

在Java的并发包中,Semaphore类表示信号量。Semaphore内部主要通过AQS
(AbstractQueuedSynchronizer)实现线程的管理。Semaphore有两个构造函数,参数permits表示许可数,它最后传递给了AQS的state值。线程在运行时首先获取许可,如果成功,许可数就减1,线程运行,当线程运行结束就释放许可,许可数就加1。如果许可数为0,则获取失败,线程位于AQS的等待队列中,它会被其它释放许可的线程唤醒。在创建Semaphore对象的时候还可以指定它的公平性。一般常用非公平的信号量,非公平信号量是指在获取许可时先尝试获取许可,而不必关心是否已有需要获取许可的线程位于等待队列中,如果获取失败,才会入列。而公平的信号量在获取许可时首先要查看等待队列中是否已有线程,如果有则入列。
Semaphore内部类:

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;            }        }    }    /**     * NonFair version     */    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);        }    }    /**     * Fair version     */    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;            }        }    }
acquire源代码
public void acquire() throws InterruptedException {    sync.acquireSharedInterruptibly(1);}public final void acquireSharedInterruptibly(int arg)        throws InterruptedException {    if (Thread.interrupted())        throw new InterruptedException();    if (tryAcquireShared(arg) < 0)        doAcquireSharedInterruptibly(arg);}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;    }}

可以看出,如果remaining <0 即获取许可后,许可数小于0,则获取失败,在doAcquireSharedInterruptibly方法中线程会将自身阻塞,然后入列。

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;    }}

可以看出释放许可就是将AQS中state的值加1。然后通过doReleaseShared唤醒等待队列的第一个节点。可以看出Semaphore使用的是AQS的共享模式,等待队列中的第一个节点,如果第一个节点成功获取许可,又会唤醒下一个节点,以此类推。

3. 使用示例

示例一:

package somhu;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Semaphore;public class SemaphoreTest implements Runnable{    private Semaphore semaphore = new Semaphore(5);    @Override    public void run() {        try {            semaphore.acquire();            Thread.sleep(2000); // 模拟耗时操作            System.err.println(Thread.currentThread().getName() + "--完成任务!");            semaphore.release();        } catch (InterruptedException e) {            e.printStackTrace();        }    }    public static void main(String[] args) {        ExecutorService exe = Executors.newFixedThreadPool(20);        SemaphoreTest task = new SemaphoreTest();        for (int i = 0; i < 20; i++) {            exe.submit(task);        }        exe.shutdown();    }}

示例二:

package javalearning;import java.util.Random;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Semaphore;public class SemaphoreDemo {    private Semaphore smp = new Semaphore(3);     private Random rnd = new Random();    class TaskDemo implements Runnable{        private String id;        TaskDemo(String id){            this.id = id;        }        @Override        public void run(){            try {                smp.acquire();                System.out.println("Thread " + id + " is working");                Thread.sleep(rnd.nextInt(1000));                smp.release();                System.out.println("Thread " + id + " is over");            } catch (InterruptedException e) {            }        }    }    public static void main(String[] args){        SemaphoreDemo semaphoreDemo = new SemaphoreDemo();        //注意我创建的线程池类型,        ExecutorService se = Executors.newCachedThreadPool();        se.submit(semaphoreDemo.new TaskDemo("a"));        se.submit(semaphoreDemo.new TaskDemo("b"));        se.submit(semaphoreDemo.new TaskDemo("c"));        se.submit(semaphoreDemo.new TaskDemo("d"));        se.submit(semaphoreDemo.new TaskDemo("e"));        se.submit(semaphoreDemo.new TaskDemo("f"));        se.shutdown();    }}

运行结果

Thread c is working

Thread b is working

Thread a is working

Thread c is over

Thread d is working

Thread b is over

Thread e is working

Thread a is over

Thread f is working

Thread d is over

Thread e is over

Thread f is over

可以看出,最多同时有三个线程并发执行,也可以认为有三个公共资源(比如计算机的三个串口)。

原创粉丝点击