Semaphore是一个二进制信号量,只有0和1两个值。如果线程想要访问一个共享资源,它必须先获得信号量。如果信号量的内部计数器大于0,那么信号量减1,并允许访问这个资源。否则,如果信号量计数器等于0,线程会等待直至计数器大于0。
所以说计数器大于0,说明有资源可用。计数器等于0,说明没有资源可用。
同时Semaphore提供了一个带有boolean参数的构造方法,true代表公平锁,false代表非公平锁,默认实现是非公平锁。
我们使用Semaphore信号量来重写PrintQueue的例子。
首先实现一个PrintQueue打印队列,有一个Semaphore信号量用来并发访问控制。打印之前使用acquire()方法获取信号量,执行完毕后使用release()方法释放信号量。每次打印等待一个随机时间,模拟打印耗时。
public class PrintQueue {
private Semaphore semaphore;
public PrintQueue() {
//semaphore = new Semaphore(1); //非公平的
semaphore = new Semaphore(1, true); //公平的
}
public void printJob(Object document) {
try {
semaphore.acquire();
long duration = (long)(Math.random() * 10000);
System.out.printf("%s: Print a job duration %d seconds.\n",
Thread.currentThread().getName(), duration / 1000);
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
}
创建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(new Object());
System.out.printf("%s: The Job has been printed.\n", Thread.currentThread().getName());
}
}
主方法类中启动10个打印Job线程。
public class Main {
public static void main(String[] args) {
PrintQueue printQueue = new PrintQueue();
Thread[] threads = new Thread[10];
for (int i = 1; i < 10; i++) {
threads[i] = new Thread(new Job(printQueue));
}
for (int i = 1; i < 10; i++) {
threads[i].start();
}
}
}
查看日志,每次只有一个打印Job可以执行打印程序,其他线程处于WAITING状态。
Thread-0: Going to print a Job.
Thread-8: Going to print a Job.
Thread-7: Going to print a Job.
Thread-6: Going to print a Job.
Thread-5: Going to print a Job.
Thread-4: Going to print a Job.
Thread-0: Print a job duration 2 seconds.
Thread-3: Going to print a Job.
Thread-2: Going to print a Job.
Thread-1: Going to print a Job.
Thread-0: The Job has been printed.
Thread-8: Print a job duration 8 seconds.
Thread-8: The Job has been printed.
Thread-7: Print a job duration 8 seconds.
Thread-7: The Job has been printed.
Thread-6: Print a job duration 9 seconds.
Thread-6: The Job has been printed.
Thread-5: Print a job duration 6 seconds.
Thread-5: The Job has been printed.
Thread-4: Print a job duration 7 seconds.
Thread-4: The Job has been printed.
Thread-3: Print a job duration 4 seconds.
Thread-3: The Job has been printed.
Thread-2: Print a job duration 1 seconds.
Thread-2: The Job has been printed.
Thread-1: Print a job duration 1 seconds.
Thread-1: The Job has been printed.