(java多线程并发)控制并发线程数的Semaphore

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ZuoAnYinXiang/article/details/50448564

1.简介
   信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们能够正确、合理的使用公共资源。

2.概念
    Semaphore分为单值和多值两种,前者只能被一个线程获得,后者可以被若干个线程获得。

以一个停车场运作为例。为了简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆不受阻碍的进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入一辆,如果又离开两辆,则又可以放入两辆,如此往复。

在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。

     更进一步,信号量的特性如下:信号量是一个非负整数(车位数),所有通过它的线程(车辆)都会将该整数减一(通过它当然是为了使用资源),当该整数值为零时,所有试图通过它的线程都将处于等待状态。在信号量上我们定义两种操作: Wait(等待) 和 Release(释放)。 当一个线程调用Wait(等待)操作时,它要么通过然后将信号量减一,要么一直等下去,直到信号量大于一或超时。Release(释放)实际上是在信号量上执行加操作,对应于车辆离开停车场,该操作之所以叫做“释放”是因为加操作实际上是释放了由信号量守护的资源。

      在java中,还可以设置该信号量是否采用公平模式,如果以公平方式执行,则线程将会按到达的顺序(FIFO)执行,如果是非公平,则可以后请求的有可能排在队列的头部。
JDK中定义如下:
Semaphore(int permits, boolean fair)
  创建具有给定的许可数和给定的公平设置的Semaphore。

     Semaphore当前在多线程环境下被扩放使用,操作系统的信号量是个很重要的概念,在进程控制方面都有应用。Java并发库Semaphore 可以很轻松完成信号量控制,Semaphore可以控制某个资源可被同时访问的个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。比如在Windows下可以设置共享文件的最大客户端访问个数。

     Semaphore实现的功能就类似厕所有5个坑,假如有10个人要上厕所,那么同时只能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中 的任何一个人让开后,其中等待的另外5个人中又有一个人可以占用了。另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项。单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合。

3.案例一:

package SemaPhore;

import java.util.Random;
import java.util.concurrent.*;
public class Test {



	public static void main(String[] args) {
		//线程池
		ExecutorService executor = Executors.newCachedThreadPool();
		//定义信号量,只能5个线程同时访问
        final Semaphore semaphore = new Semaphore(5);
        //模拟20个线程同时访问
        for (int i = 0; i < 20; i++) {
        	 final int NO = i;
			 Runnable runnable = new Runnable() {
				public void run() {
					try {
						//获取许可
						semaphore.acquire();
						//availablePermits()指的是当前信号灯库中有多少个可以被使用
						System.out.println("线程" + Thread.currentThread().getName() +"进入,当前已有" + (5-semaphore.availablePermits()) + "个并发");
					    System.out.println("index:"+NO);
						Thread.sleep(new Random().nextInt(1000)*10);
					    
						System.out.println("线程" + Thread.currentThread().getName() + "即将离开");	
					    //访问完后,释放
					    semaphore.release();

					
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			};
			
			executor.execute(runnable);
        }
        // 退出线程池
        executor.shutdown();
	}

}


4.案例二:

下面是模拟一个连接池,控制同一时间最多只能有50个线程访问。

[java] view plaincopy
  1. import java.util.UUID;  
  2. import java.util.concurrent.Semaphore;  
  3. import java.util.concurrent.TimeUnit;  
  4.   
  5. public class TestSemaphore extends Thread {  
  6.     public static void main(String[] args) {  
  7.         int i = 0;  
  8.         while (i < 500) {  
  9.             i++;  
  10.             new TestSemaphore().start();  
  11.             try {  
  12.                 Thread.sleep(1);  
  13.             } catch (InterruptedException e) {  
  14.                 e.printStackTrace();  
  15.             }  
  16.         }  
  17.     }  
  18.   
  19.     /** 
  20.      * 控制某资源同时被访问的个数的类 控制同一时间最后只能有50个访问 
  21.      */  
  22.     static Semaphore semaphore = new Semaphore(50);  
  23.     static int timeout = 500;  
  24.   
  25.     public void run() {  
  26.         try {  
  27.             Object connec = getConnection();  
  28.             System.out.println("获得一个连接" + connec);  
  29.             Thread.sleep(300);  
  30.             releaseConnection(connec);  
  31.         } catch (InterruptedException e) {  
  32.             e.printStackTrace();  
  33.         }  
  34.     }  
  35.   
  36.     public void releaseConnection(Object connec) {  
  37.         /* 释放许可 */  
  38.         semaphore.release();  
  39.         System.out.println("释放一个连接" + connec);  
  40.     }  
  41.   
  42.     public Object getConnection() {  
  43.         try {/* 获取许可 */  
  44.             boolean getAccquire = semaphore.tryAcquire(timeout, TimeUnit.MILLISECONDS);  
  45.             if (getAccquire) {  
  46.                 return UUID.randomUUID().toString();  
  47.             }  
  48.         } catch (InterruptedException e) {  
  49.             e.printStackTrace();  
  50.         }  
  51.         throw new IllegalArgumentException("timeout");  
  52.     }  
  53. }  











展开阅读全文

没有更多推荐了,返回首页