Java线程之Semaphore

之前一直对Semaphore不是很理解,感觉它功能十分简单,用Lock加Condition完全可以轻易替代它,觉得它根本没有存在的必要。最近遇到一个例子,用Semaphore可以优雅的实现,用Lock加Condition却让我大费脑筋,主要原因是自己水平太菜。记录下来,免得以后忘了。

例子:某单位有3台打印机,有10个用户提交打印任务,写程序模拟对打印机的管理。不废话,直接上代码

public class Main {
	//主程序,很简单,开启10个线程,每个线程是一个打印任务
	//线程间共享资源是PrintQueue,即打印机队列,负责对打印机进行管理
	public static void main(String[] args) {
		final PrintQueue printQueue = new PrintQueue();
		Thread[] threads = new Thread[10];
		for(int i=0; i<10; i++) {
			threads[i] = new Thread(new Job(printQueue), "线程"+i);
		}
		for(int i=0; i<10; i++) {
			threads[i].start();
		}
	}
}

//线程的具体实现,也很简单,就是调用PrintQueue方法打印一些内容
//具体打印什么内容都省了,随便模拟一下
public class Job implements Runnable {

	private final PrintQueue printQueue;
	
	public Job(final PrintQueue printQueue) {
		this.printQueue = printQueue;
	}

	public void run() {
		System.out.printf("%s: 提交打印任务\n",
				Thread.currentThread().getName());
		printQueue.printJob();
		System.out.printf("%s: 向用户汇报:打印完毕!~\n",
				Thread.currentThread().getName());
	}
}

用信号量Semaphore实现打印机队列

public class PrintQueue {

	private final Semaphore semaphore;
	private boolean[] freePrinters;
	private Lock lockPrinters;
	
	public PrintQueue() {
		semaphore = new Semaphore(3);
		freePrinters = new boolean[]{true, true, true};
		lockPrinters = new ReentrantLock();
	}
	
	public void printJob() {
		int assignedPrinter = -1;
		try {
			semaphore.acquire();
			assignedPrinter = getPrinter();
			long duration = (long)(Math.random()*9) + 1;
			System.out.printf("%s: %d 号打印机打印了第1部分内容,耗时  %d 秒\n",
					Thread.currentThread().getName(), assignedPrinter,duration);
			TimeUnit.SECONDS.sleep(duration);
			duration = (long)(Math.random()*9) + 1;
			System.out.printf("%s: %d 号打印机打印了第2部分内容,耗时  %d 秒\n",
					Thread.currentThread().getName(), assignedPrinter,duration);
			TimeUnit.SECONDS.sleep(duration);
			duration = (long)(Math.random()*9) + 1;
			System.out.printf("%s: %d 号打印机打印了第3部分内容,耗时  %d 秒\n",
					Thread.currentThread().getName(), assignedPrinter,duration);
			TimeUnit.SECONDS.sleep(duration);
		}catch(InterruptedException e) {
			e.printStackTrace();
		}finally {
			releasePrinter(assignedPrinter);
		}
	}
	
	private int getPrinter() {
		int ret = -1;
		lockPrinters.lock();
		for(int i=0; i<freePrinters.length; i++) {
			if(freePrinters[i] == true) {
				ret = i;
				freePrinters[i] = false;
				break;
			}
		}
		lockPrinters.unlock();
		return ret;
	}
	
	private void releasePrinter(int index) {
		freePrinters[index] = true;
		semaphore.release();
	}
}

改用Lock和Condition实现打印机队列

public class PrintQueue {

	private boolean[] freePrinters;
	private Lock lockPrinters;
	private Condition cond;
	
	public PrintQueue() {
		freePrinters = new boolean[]{true, true, true};
		lockPrinters = new ReentrantLock();
		cond = lockPrinters.newCondition();
	}
	
	public void printJob() {
		int assignedPrinter = getPrinter();
		try {
			long duration = (long)(Math.random()*9) + 1;
			System.out.printf("%s: %d 号打印机打印了第1部分内容,耗时  %d 秒\n",
					Thread.currentThread().getName(), assignedPrinter,duration);
			TimeUnit.SECONDS.sleep(duration);
			duration = (long)(Math.random()*9) + 1;
			System.out.printf("%s: %d 号打印机打印了第2部分内容,耗时  %d 秒\n",
					Thread.currentThread().getName(), assignedPrinter,duration);
			TimeUnit.SECONDS.sleep(duration);
			duration = (long)(Math.random()*9) + 1;
			System.out.printf("%s: %d 号打印机打印了第3部分内容,耗时  %d 秒\n",
					Thread.currentThread().getName(), assignedPrinter,duration);
			TimeUnit.SECONDS.sleep(duration);
		}catch(InterruptedException e) {
			e.printStackTrace();
		}finally {
			releasePrinter(assignedPrinter);
		}
	}
	
	private int getPrinter() {
		int ret = -1;
		lockPrinters.lock();
		while(true) {
			for(int i=0; i<freePrinters.length; i++) {
				if(freePrinters[i] == true) {
					ret = i;
					freePrinters[i] = false;
					break;
				}
			}
			if(ret>=0) {
				break;
			}else {
				try {
					cond.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		lockPrinters.unlock();
		return ret;
	}
	
	private void releasePrinter(int index) {
		lockPrinters.lock();
		freePrinters[index] = true;
		cond.signalAll();
		lockPrinters.unlock();
	}
}
比较后可见,使用Lock加Condition需要写更多的代码,而且逻辑上不是特别清晰,Semaphore的使用是十分必要的。

另一种使用Lock加Condition的实现

public class PrintQueue {

	private boolean[] freePrinters;
	private Lock lockPrinters;
	private Lock permitLock;
	private Condition cond;
	private int printerCount;
	
	public PrintQueue() {
		freePrinters = new boolean[]{true, true, true};
		lockPrinters = new ReentrantLock();
		permitLock = new ReentrantLock();
		cond = permitLock.newCondition();
		printerCount = 3;
	}
	
	public void printJob() {
		getPermit();
		int assignedPrinter = getPrinter();
		try {
			long duration = (long)(Math.random()*9) + 1;
			System.out.printf("%s: %d 号打印机打印了第1部分内容,耗时  %d 秒\n",
					Thread.currentThread().getName(), assignedPrinter,duration);
			TimeUnit.SECONDS.sleep(duration);
			duration = (long)(Math.random()*9) + 1;
			System.out.printf("%s: %d 号打印机打印了第2部分内容,耗时  %d 秒\n",
					Thread.currentThread().getName(), assignedPrinter,duration);
			TimeUnit.SECONDS.sleep(duration);
			duration = (long)(Math.random()*9) + 1;
			System.out.printf("%s: %d 号打印机打印了第3部分内容,耗时  %d 秒\n",
					Thread.currentThread().getName(), assignedPrinter,duration);
			TimeUnit.SECONDS.sleep(duration);
		}catch(InterruptedException e) {
			e.printStackTrace();
		}finally {
			releasePrinter(assignedPrinter);
			releasePermit();
		}
	}
	
	private int getPrinter() {
		int ret = -1;
		lockPrinters.lock();
		for(int i=0; i<freePrinters.length; i++) {
			if(freePrinters[i] == true) {
				ret = i;
				freePrinters[i] = false;
				break;
			}
		}
		lockPrinters.unlock();
		return ret;
	}
	
	private void getPermit() {
		try {
			permitLock.lock();
			while(printerCount<=0) {
					cond.await();
			}
			printerCount--;
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			permitLock.unlock();
		}
	}
	
	private void releasePermit() {
		try {
			permitLock.lock();
			printerCount++;
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			cond.signalAll();
			permitLock.unlock();
		}
	}
	
	private void releasePrinter(int index) {
		freePrinters[index] = true;	//原子操作,无需同步
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值