Semaphore信号量浅析

前言

Semaphore对于线程来说相当于售票员,线程想继续执行,就需要向Semaphore买票,买到票则向下执行,没有买到则排队等待,其中对于每个线程来说有的线程需要买n多张票才能执行,Semaphore的用法类似与这个说法

方法说明

state的值对应state数量的许可证

 public Semaphore(int permits)
 创建state等于permits的非公平Semaphore
 public Semaphore(int permits, boolean fair)
 创建可选的公平/非公平的state等于permits的Semaphore
 public void acquire() throws InterruptedException
 申请一个许可证,申请不到则阻塞,可以被中断
 public void acquireUninterruptibly()
 申请一个许可证,申请不到则阻塞,不可以被中断
 public boolean tryAcquire()
 尝试申请一个许可证,申请不到返回false,申请的返回true
 public boolean tryAcquire(long timeout, TimeUnit unit)
    throws InterruptedException
 尝试在一段时间内申请一个许可证,申请不到返回false,申请的返回true  
 public void release() 
 归还一个许可证
 ------------  与上面方法类似,只不过上面是11个的,这里是大于0的整数  ------------
 |public void acquire(int permits) throws InterruptedException                |
 |public void acquireUninterruptibly(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()
 获取state的值
 public int drainPermits()
 设置state的值为0
 public boolean isFair()  
 获取当前Semaphore是否公平 
 public final boolean hasQueuedThreads()
 获取是否有线程等待在当前Semaphore上(不准改数量可变的,只具有参考价值)
 public final int getQueueLength()
 获取等待在Semaphore上线程的数量(不准改数量可变的,只具有参考价值)

源码分析

Semaphore也是基于AQS的共享模式,即Semaphore内部有一个继承AbstractQueuedSynchronizer的类Sync,并重写tryReleaseShared和tryAcquireShared方法。
与CountDownLatch不同的是:
tryAcquireShared:修改成功state的值,则返回1,否则返回-1,对AQS来说tryAcquireShared方法是用来判断线程是否可以出队列和入队列的判断方法,即正数表示当前线程不要排队
tryReleaseShared方法: 一般的实现方式是修改state的值,并返回修改成功与否的状态。返回的状态主要是用来判断当前锁是否可用,可用则唤醒CLH队列中的节点线程

我们知道AbstractQueuedSynchronizer对于Semaphore的实现就是state用来记录许可证的数目,而双向等待队列用来记录排队的线程

公平/非公平就是:每当一个线程需要获取许可证的时候,公平的执行方式,应该首先判断队列中是否有线程在等待,队列有则将当前线程入队等待
公平

protected int tryAcquireShared(int acquires) {
     for (;;) {
     /**
         public final boolean hasQueuedPredecessors() {
           Node t = tail; 
           Node h = head;
           Node s;
           return 
           是否有节点的检查
           h != t &&
           节点线程的检查
            ((s = h.next) == null || s.thread != Thread.currentThread());
       }
     */
         if (
             判断:队列中是否有等待线程
             hasQueuedPredecessors()
             )
             return -1;
         下面就是些CAS的赋值操作    
         int available = getState();
         int remaining = available - acquires;
         if (remaining < 0 ||
             compareAndSetState(available, remaining))
             return remaining;
     }
 }

非公平(与上述实现类似只是没有了等待节点的检查 hasQueuedPredecessors方法 )

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

acquire方法与CountDownLatch的await方法类似,只是它们二者tryAcquireShared上不同,
CountDownLatch的不修改state的值
Semaphore是有可能修改state的值

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

Semaphore的复用性

该方法直接增加state的值,即总是state开始时等于1,你也可以归还许可证,使其值等于2(or其他)
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;
    }
}

总结:Semaphore的用法和原理很简单,只要知道Semaphore相当于一个检票员,每个线程只有向其出示需要的门票才能继续执行而不是等待,同时Semaphore也是可以自己生产票,只要调用release方法传入的参数n是正数就能新生n张门票

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Windows系统中,信号量Semaphore)是一种同步对象,用于在多个线程之间协调共享资源的访问。Windows提供了一组API函数来创建、操作和销毁信号量。 以下是一些与信号量相关的API函数: 1. CreateSemaphore:创建一个信号量对象。 2. WaitForSingleObject:等待一个信号量对象。 3. ReleaseSemaphore:释放一个信号量对象。 4. OpenSemaphore:打开一个已经存在的信号量对象。 5. CloseHandle:关闭一个信号量对象的句柄。 下面是一个使用信号量的示例: ```C++ #include <windows.h> #include <iostream> using namespace std; HANDLE hSemaphore; // 信号量句柄 DWORD WINAPI ThreadProc(LPVOID lpParam) { // 等待信号量 WaitForSingleObject(hSemaphore, INFINITE); // 访问共享资源 cout << "Thread " << GetCurrentThreadId() << " access shared resource." << endl; // 释放信号量 ReleaseSemaphore(hSemaphore, 1, NULL); return 0; } int main() { // 创建信号量 hSemaphore = CreateSemaphore(NULL, 1, 1, NULL); // 创建线程 HANDLE hThread1 = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL); HANDLE hThread2 = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL); // 等待线程结束 WaitForSingleObject(hThread1, INFINITE); WaitForSingleObject(hThread2, INFINITE); // 关闭信号量句柄 CloseHandle(hSemaphore); return 0; } ``` 在这个示例中,我们创建了一个信号量句柄,并创建了两个线程。线程会等待信号量,然后访问共享资源。访问完成后,线程会释放信号量。由于信号量的初始计数为1,所以只有一个线程能够访问共享资源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值