Semaphore的作用:
在java中,使用了synchronized关键字和Lock锁实现了资源的并发访问控制,在同一时间只允许唯一了线程进入临界区访问资源(读锁除外),这样子控制的主要目的是为了解决多个线程并发同一资源造成的数据不一致的问题。在另外一种场景下,一个资源有多个副本可供同时使用,比如打印机房有多个打印机、厕所有多个坑可供同时使用,这种情况下,Java提供了另外的并发访问控制--资源的多副本的并发访问控制,今天学习的信号量Semaphore即是其中的一种。
package com.np.ota.test.semaphore;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 打印室
* Semaphore:信号量,允许指定最多几个线程访问某个资源,
* 例如一个打印室里面有3台打印机可以同时打印,那么打印队列可以同时有三个任务在打印
* @author luke
*/
public class PrintRoom {
private final Semaphore semaphore;
/**多台打印机*/
private boolean[] freePrinters;
private Lock lockPrinters;
public PrintRoom(int limitSize) {
semaphore = new Semaphore(limitSize,true);
freePrinters = new boolean[limitSize];
for(int i = 0; i < limitSize; i++){
freePrinters[i] = true;
}
lockPrinters = new ReentrantLock();
}
public void printJob(Object document){
try {
semaphore.acquire();
int assignedPrinter = getPrinter();
//这里存在多线程访问(个数为信号量限制大小),所以启动各自线程执行
new Thread(new DoJob(semaphore,freePrinters, assignedPrinter)).start();
} catch (InterruptedException e) {
e.printStackTrace();
}
/*finally{
semaphore.release();
}*/
}
/**
* 获取空闲打印机,这里要用锁控制并发访问(并发数量是信号量大小)
* @return
*/
private int getPrinter(){
lockPrinters.lock();
int ret = -1;
try {
for(int i = 0; i < freePrinters.length; i++){
if(freePrinters[i]){
ret = i;
freePrinters[i] = false;
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}finally{
lockPrinters.unlock();
}
return ret;
}
class DoJob implements Runnable {
private Semaphore semaphore;
private boolean[] freePrinterss;
private int assigned;
public DoJob(Semaphore semaphore,boolean[] freePrinters, int assignedPrinter) {
this.semaphore = semaphore;
this.freePrinterss = freePrinters;
this.assigned = assignedPrinter;
}
@Override
public void run() {
try {
//执行打印任务
long duration = (long)(Math.random()*10+1);
System.out.println(Thread.currentThread().getName()+",用打印机:"+assigned+",需要花时长:"+duration);
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
//打印任务结束,允许新的打印任务进入打印室
freePrinterss[assigned] = true;
semaphore.release();
}
}
}
}
package com.np.ota.test.semaphore;
/**
* 模拟一个线程任务,执行打印
* @author luke
*/
public class Job implements Runnable{
private PrintRoom printQueue;
public Job(PrintRoom printQueue) {
this.printQueue = printQueue;
}
@Override
public void run() {
printQueue.printJob(new Object());
}
}
package com.np.ota.test.semaphore;
public class Main {
public static void main(String[] args) {
//打印室,有3台打印机
int limitSize = 3;
PrintRoom printRoom = new PrintRoom(limitSize);
//一百个打印任务
Thread[] threads = new Thread[100];
for(int i =0; i < 100; i++){
threads[i] = new Thread(new Job(printRoom),"Thread"+i);
}
for(int i =0; i < 100; i++){
threads[i].start();
}
}
}
结果:
Thread-0,用打印机:0,需要花时常:7
Thread-1,用打印机:1,需要花时常:2
Thread-2,用打印机:2,需要花时常:5
Thread-3,用打印机:1,需要花时常:7
Thread-4,用打印机:2,需要花时常:4
Thread-5,用打印机:0,需要花时常:6
Thread-6,用打印机:2,需要花时常:10
Thread-7,用打印机:1,需要花时常:5
Thread-8,用打印机:0,需要花时常:8
Thread-9,用打印机:1,需要花时常:6
Thread-10,用打印机:2,需要花时常:8
Thread-11,用打印机:0,需要花时常:9
Thread-12,用打印机:1,需要花时常:7
Thread-13,用打印机:2,需要花时常:7
Thread-15,用打印机:1,需要花时常:2
Thread-14,用打印机:0,需要花时常:5
Thread-16,用打印机:1,需要花时常:1
Thread-17,用打印机:1,需要花时常:7
Thread-19,用打印机:2,需要花时常:7
Thread-18,用打印机:0,需要花时常:7
Thread-20,用打印机:1,需要花时常:4
Thread-21,用打印机:0,需要花时常:3
。。。