Java为什么使用信号量?

问题描述

从我读到的理论来看,有两种类型

1) Binary :-0或1作为值,或者换句话说,只有一个线程允许进入关键部分(或者我可能没有正确解释它)

2)计数:-仅允许n个线程中的k个访问k个资源[k =资源数量,n =线程数量始终为n> k]

  1. 为什么要使用二进制信号量?

如果是二进制信号量,即

Semaphore sem = new Semaphore(1); 

用于只允许一个thead进入的地方,它们是否与任何普通的LOCK都做同样的事情?即只允许一个线程进入内部,然后如何进入

sem.aquire(); //since count=1 only one thread enters
try
{
 //Important Stuff which can be accessed only by one thread
}
finally{sem.release();}

有什么不同吗?

lock.lock()
try
{
 //Again only one thread allowed here same as binary semaphore
}
finally{lock.unlock();}
  1. 为什么要计数信号量?

现在让我们说有

a)3个资源R1,R2,R3和一个资源一次只能由1个线程使用

b)9个线程T1,T2 ..... T9

c)这是每个线程如何使用这些资源

-> T1,T2,T3 =>想要R1

-> T4,T5,T6 =>想要R2

-> T5,T6,T7 =>想要R3

一个人会说,对信号量进行计数将只允许3个线程最多获取3个资源,如果每个线程都试图获取一个唯一的资源,这将很好地工作,但显然在此示例中情况并非如此。可以说,如果T1,T2,T3都进入了关键部分,那么T1得到R1,并且R1现在已从可用资源列表中删除。现在T2,T3将崩溃,因为R1现在不可用。

您可以锁定资源的每种方法,而不是将其从资源队列中删除,以确保是否共享一个资源,只有一个线程可以使用它,但这不仅破坏了信号量的目的,而且现在我们同时具有信号灯和锁的开销。

信号量如何解决N个线程访问1个资源的问题,即N-> k,其中k是k1,k2,k3 ... kn个资源队列中的资源?

我的解决方

并不完美,但不需要锁并且很公平

private final class Resource
 {
  
  private static final HashMap<Integer,Resource> instances=new HashMap(); //An map keeping track of available resources
  private static final Object aquireLock=new Object(); //allow only one thread to aquire an resource and add itself to the waiting queue if it was not available at a time
  private static final ArrayList<QueueObject> waiting=new ArrayList();//list of threads waiting to aquire an resource
  
  private static void init()  //Only 3 resources
  {   
   instances.put(0,new Resource());
   instances.put(1,new Resource());
   instances.put(2,new Resource());
  }
  
  private static void use(int index,Consumer<Resource> action)
  {
   Resource res=null;
   
   boolean doWait=false;
   synchronized(aquireLock)
   {
    if(instances.containsKey(index)){res=instances.remove(index);}//remove resource to make it unavailable
    else{doWait=true;}//not available? then wait
   }
   if(doWait)
   {
    QueueObject wait=new QueueObject(index); //store the name[index] of the resource it was asking so it can be woken up when that resource is available
    
    synchronized(waiting){waiting.add(wait);} //Now wait in queue
    
    wait.doWait();
    
    waiting.remove(wait);//wait over remove itself from queue
   }
   if(res==null){res=instances.remove(index);}//after wait again try to get resource[if it already has resource res wont be null]
   
   try{action.accept(res);}//do stuff with resource
   finally
   {
    synchronized(aquireLock){instances.put(index,res);}//Resource utilized Now return it back to the queue
     
    synchronized(waiting)
    {  
     waiting.stream()
            .filter(obj->obj.resID==index) //find the 1st thread in queue that was asking for this resource and wake it up so it can use it Now
            .findFirst()
            .ifPresent(QueueObject::doNotify);
    }
   }
  }
  
  private static final class QueueObject
  {
   private boolean isNotified = false;
   private final int resID;
   
   private QueueObject(int resID){this.resID=resID;}
   
   private synchronized void doWait() 
   {
    while(!isNotified) //protection against missed signals and spurIoUs wake up's
    {
     try{this.wait();}
     catch(InterruptedException ex){}
    }
    this.isNotified = false;
   } 

   private synchronized void doNotify() 
   {
     this.isNotified = true;
     this.notify();
   }
  }
}

因此,使用二进制信号量执行相同的操作时,将锁定并计数信号量,该问题具有保护1个资源(在k个资源的队列中)免受N个线程影响的问题?

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)