控制并发线程数的Semaphore

一、简介和构造方法

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


二、构造方法:

public Semaphore(int permits)

public Semaphore(int permits, boolean fair)

其中第二种构造方法中的第二个参数fair可以指定是否公平


在构造信号量对象时,必须要指定洗好了的准入数,即同时能申请多少个许可。

当每个线程每次要申请一个许可时,这就相当于指定了同时有多少个线程可以访问某一资源。


三、信号量的主要逻辑方法

public void acquire()

public void acquireUninterruptibly()

public boolean tryAcquire()

public boolean tryAcquire(long timeout, TimeUnit unit)

public void release()

具体用法:

acquire()方法尝试获得一个准入的许可。若无法获取,则线程会等待,直到有线程释放一个许可或者当前线程被中断

acquireUninterruptibly()方法和acquire()方法类似,但是不响应中断

tryAcquire()方法尝试获得一个许可,如果成功则返回true,失败则返回false它不会进行等待

release()方法用来在线程访问资源结束后释放一个许可,以使其他等待许可的线程可以进行资源访问。


示例如下:

import java.util.concurrent.*;

public class SemaphoreTest implements Runnable {
	
	final Semaphore semp = new Semaphore(5);
	
	@Override
	public void run() {
		try {
			semp.acquire();
			Thread.sleep(1000);
			System.out.println(Thread.currentThread().getId() + ":done!");
		}catch (InterruptedException e) {
			e.printStackTrace();
		}finally {
			semp.release();
		}
	}

	public static void main(String[] args) {
		ExecutorService exec = Executors.newFixedThreadPool(20);
		SemaphoreTest semp = new SemaphoreTest();
		for(int i = 0; i < 20; i ++) {
			exec.submit(semp);
		}
	}

}复制代码

注意:申请信号量使用acquire()方法操作,在离开时,务必使用release()方法释放信号量

这就和释放锁一个道理,如果不幸发生了信号量的泄漏申请了但没有释放),那么可以进入临界区的线程数量就会越来越少,直到所有的线程均无法访问。


四、使用信号量完成多线程按顺序输出打印

要求:需要三个线程分别循环按顺序打印出a,b,c,d,e,f,g,h,i,即线程1打印a,d,g;线程2打印b,e,h;线程3打印c,f,i。

import java.util.concurrent.*;

public class SemaphoreTest2 {
	
	static Semaphore semp1 = new Semaphore(1);
	static Semaphore semp2 = new Semaphore(1);
	static Semaphore semp3 = new Semaphore(1);
	static int n = 3;
	
	static class runnable1 implements Runnable {
		@Override
		public void run() {
			char beginChar = 'a';
			for(int i = 0; i < n; i ++) {
				try {
					if(i == 0) {
						System.out.println((char)(beginChar + i * 3));
						semp2.release();
					}else {
						semp1.acquire();
						System.out.println((char)(beginChar + i * 3));
						semp2.release();
					}
				}catch(InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	static class runnable2 implements Runnable {
		@Override
		public void run() {
			char beginChar = 'b';
			for(int i = 0; i < n; i ++) {
				try {
					semp2.acquire();
					System.out.println((char)(beginChar + i * 3));
					semp3.release();
				}catch(InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	static class runnable3 implements Runnable {
		@Override
		public void run() {
			char beginChar = 'c';
			for(int i = 0; i < n; i ++) {
				try {
					if(i == n - 1) {
						semp3.acquire();
						System.out.println((char)(beginChar + i * 3));
					}else {
						semp3.acquire();
						System.out.println((char)(beginChar + i * 3));
						semp1.release();
					}
				}catch(InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public static void main(String[] args) {
		try {
			semp1.acquire();
			semp2.acquire();
			semp3.acquire();
		}catch(InterruptedException e) {
			e.printStackTrace();
		}
		Thread thread1 = new Thread(new runnable1());
		Thread thread2 = new Thread(new runnable2());
		Thread thread3 = new Thread(new runnable3());
		thread1.start();
		thread2.start();
		thread3.start();
	}

}
复制代码


猜你喜欢

转载自juejin.im/post/5de3b97151882516e524b560