Java并发之Semaphore

    Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。

一、方法摘要

  • Semaphore(int permits):构造方法,创建具有给定的许可数和非公平锁的Semaphore。
  • Semaphore(int permits, Boolean fair):构造方法,创建具有给定许可数和给定公平设置的Semaphore。
  • void acquire():从此信号量获取一个许可,在提供一个许可前将阻塞,否则线程被中断。
  • void acquire(int permits):从此信号量获取给定数目的许可,在提供这些许可前阻塞,或者线程已被中断。
  • void acquireUninterruptibly():获取许可,无视中断。
  • Boolean tryAcquire():仅在调用此信号量时存在一个许可,才从信号量获取许可。
  • void release():释放一个许可,将其返回给信号量。

二、使用方法

    Semaphore是一个计数信号量。从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个acquire(),然后再获取许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。

Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。例如,下面的类使用信号量控制对内容池的访问:

public class SemaphoreTest {

	private static final int THREAD_COUNT = 30;

	private static ExecutorService threadPool = Executors
			.newFixedThreadPool(THREAD_COUNT);

	private static Semaphore s = new Semaphore(10);

	public static void main(String[] args) {
		for (int i = 0; i < THREAD_COUNT; i++) {
			threadPool.execute(new Runnable() {
				@Override
				public void run() {
					try {
						s.acquire();				//获取许可
						System.out.println("save data");
						s.release();				//释放许可
					} catch (InterruptedException e) {
					}
				}
			});
		}

		threadPool.shutdown();
	}
}

    在代码中,虽然有30个线程在执行,但是只允许10个并发的执行。Semaphore的构造方法Semaphore(int permits) 接受一个整型的数字,表示可用的许可证数量。Semaphore(10)表示允许10个线程获取许可证,也就是最大并发数是10。Semaphore的用法也很简单,首先线程使用Semaphore的acquire()获取一个许可证,使用完之后调用release()归还许可证。还可以用tryAcquire()方法尝试获取许可证。

三、实现原理

    结构类图

    信号量内含了两个队列同步器,分别是公平的和非公平的。通过队列同步器来控制线程同步。

    构造函数

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

    Semaphore内置了两个队列同步器,分别为公平的和非公平的。根据构造参数的不同,决定实现哪一个同步器。

    acquire 方法

    acquire()方法用来获取许可,这里的许可其实关联到队列同步器AQS的状态state。源码如下:

    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);	//尝试失败 则 继续等待
    }

    该方法为模板方法,需要对tryAcquireShared方法进行重写。对于非公平锁,源码如下:

        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;				//返回新的状态值
            }
        }

    对于公平锁,则需要加上判断,该线程是否为同步队列的头结点才可。acquire本质上是交出同步器的state给线程。当state消耗完时(<=0)就无法再获得共享锁。

    release 方法

    release方法用来归还信号量。源码如下:

    public void release() {
        sync.releaseShared(1);	//调用同步器的释放共享锁
    }

    该方法调用了同步器的模板方法:

    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {	//尝试释放锁
            doReleaseShared();
            return true;
        }
        return false;
    }
    在Semaphore中,重写了tryReleaseShared方法:

        protected final boolean tryReleaseShared(int releases) {
            for (;;) {
                int current = getState();				//获取state值
                int next = current + releases;				//归还信号量
                if (next < current) // overflow
                    throw new Error("Maximum permit count exceeded");
                if (compareAndSetState(current, next))			//更新state	
                    return true;
            }
        }


       
   



猜你喜欢

转载自blog.csdn.net/u010771890/article/details/75267140
今日推荐