这一篇博文是【大数据技术●降龙十八掌】系列文章的其中一篇,点击查看目录:大数据技术●降龙十八掌
信号量(Semaphore)是可以定义共享资源的个数,允许多个线程同时使用共享资源,在信号量内部有一个计数器,当有线程访问资源时候,计数器将自动递减,当它为0时,不再允许其他线程对共享资源访问,只到有一个线程释放共享资源,这样就完成就共享资源的保护。定义N个长度信号量(Semaphore)可以理解为定义了N个令牌,每个线程只有拿到令牌后才能使用资源,当使用资源完毕后,归还令牌。
看一个实例:
import java.util.Date;
import java.util.concurrent.Semaphore;
/**
* Created by 鸣宇淳 on 2017/12/26.
*/
public class Demo1 {
//定义一个20长度的Semaphore信号量
private static Semaphore semaphore = new Semaphore(20);
public static void main(String[] args) {
ThreadDemo threadDemo1 = new ThreadDemo(semaphore, 10);
ThreadDemo threadDemo2 = new ThreadDemo(semaphore, 16);
ThreadDemo threadDemo3 = new ThreadDemo(semaphore, 5);
Thread t1 = new Thread(threadDemo1, "线程1");
Thread t2 = new Thread(threadDemo2, "线程2");
Thread t3 = new Thread(threadDemo3, "线程3");
t1.start();
t2.start();
t3.start();
}
static class ThreadDemo implements Runnable {
private int num;
private Semaphore semaphore;
public ThreadDemo(Semaphore semaphore, int num) {
this.num = num;
this.semaphore = semaphore;
}
public void run() {
try {
System.out.println(new Date() + Thread.currentThread().getName() + "申请获得" + this.num + "个令牌");
//占用num个令牌
semaphore.acquire(this.num);
System.out.println(new Date() + Thread.currentThread().getName() + "[已经]得到" + this.num + "个令牌.......");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放num个令牌
semaphore.release(this.num);
System.out.println(new Date() + Thread.currentThread().getName() + "释放" + this.num + "个令牌++++++++");
}
}
}
}
输出:
Tue Dec 26 19:03:26 CST 2017线程2申请获得16个令牌
Tue Dec 26 19:03:26 CST 2017线程1申请获得10个令牌
Tue Dec 26 19:03:26 CST 2017线程3申请获得5个令牌
Tue Dec 26 19:03:26 CST 2017线程2[已经]得到16个令牌.......
Tue Dec 26 19:03:28 CST 2017线程2释放16个令牌++++++++
Tue Dec 26 19:03:28 CST 2017线程3[已经]得到5个令牌.......
Tue Dec 26 19:03:28 CST 2017线程1[已经]得到10个令牌.......
Tue Dec 26 19:03:30 CST 2017线程1释放10个令牌++++++++
Tue Dec 26 19:03:30 CST 2017线程3释放5个令牌++++++++
这个例子中,一共有20个令牌,线程1、线程2、线程3都去申请令牌,线程2首先领到16个令牌,还剩下4个,不满足线程2和线程1的需求,他们两个只能等待,当线程2释放了16个令牌后,首先分配给线程3令牌,还剩下17个,也满足线程1的10个需求,所以也分配给了线程1,当他们使用结束后,就都归还了令牌。
领取令牌使用acquire()方法,归还令牌使用release()方法,一定要记得将归还令牌的代码放入finally中,以保证肯定能归还。