控制并发访问资源的多个副本
在前面的记录中使用binary semaphore实现了一个例子,这个例子是用来保护一个共享资源;但是semaphore也可以用来保护可以同时被多个线程访问的多个共享资源副本或者临界区副本;
在下面的例子中展示使用semaphore去保护多个资源的副本;
动手实现
1.创建一个PrintQueue
public class PrintQueue {
// present printer that are free to print a job
private boolean freePrinters[];
// Use this lock to protect the access to the freePrinters.
private Lock lockPrinters;
private final Semaphore semaphore;
public PrintQueue() {
this.semaphore = new Semaphore(3);
freePrinters = new boolean[3];
for (int i = 0; i < 3; i++) {
freePrinters[i]=true;
}
lockPrinters=new ReentrantLock();
}
public void printJob(){
try {
semaphore.acquire();
int assignedPrinter=getPrinter();
long duration=(long)(Math.random()*10);
System.out.printf("%s: PrintQueue: Printing a Job in Printer %d during %d seconds\n",
Thread.currentThread().getName(),assignedPrinter,duration);
TimeUnit.SECONDS.sleep(duration);
freePrinters[assignedPrinter]=true;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();
}
}
private int getPrinter(){
int ret=-1;
lockPrinters.lock();
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;
}
}
2.创建一个job
public class Job implements Runnable {
private PrintQueue printQueue;
public Job(PrintQueue printQueue) {
this.printQueue = printQueue;
}
@Override
public void run() {
System.out.printf("%s: Going to print a job\n",
Thread.currentThread().getName());
printQueue.printJob();
System.out.printf("%s: The document has been printed\n",
Thread.currentThread().getName());
}
}
3.Main
public class Main {
public static void main(String[] args) {
PrintQueue printQueue=new PrintQueue();
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(new Job(printQueue), "Thread_" + i);
}
for (int i = 0; i < 10; i++) {
threads[i].start();
}
}
}
要点
在上面的printJob方法中,semaphore每次可以是三个线程同时访问,多余的线程将被阻塞;而进入的这三个线程又形成了竞争,而这里使用lock进一步去保护共享数据,标记每一次可以访问的线程,把索引记录在freePrinters中;