- Semaphore的作用:
- 在java中,使用了synchronized关键字和Lock锁实现了资源的并发访问控制,在同一时间只允许唯一了线程进入临界区访问资源(读锁除外),这样子控制的主要目的是为了解决多个线程并发同一资源造成的数据不一致的问题。在另外一种场景下,一个资源有多个副本可供同时使用,比如打印机房有多个打印机、厕所有多个坑可供同时使用,这种情况下,Java提供了另外的并发访问控制–资源的多副本的并发访问控制,今天学习的信号量Semaphore即是其中的一种。
- Semaphore实现原理初探:
- Semaphore是用来保护一个或者多个共享资源的访问,Semaphore内部维护了一个计数器,其值为可以访问的共享资源的个数。一个线程要访问共享资源,先获得信号量,如果信号量的计数器值大于1,意味着有共享资源可以访问,则使其计数器值减去1,再访问共享资源。
- 如果计数器值为0,线程进入休眠。当某个线程使用完共享资源后,释放信号量,并将信号量内部的计数器加1,之前进入休眠的线程将被唤醒并再次试图获得信号量。
- 就好比一个厕所管理员,站在门口,只有厕所有空位,就开门允许与空侧数量等量的人进入厕所。多个人进入厕所后,相当于N个人来分配使用N个空位。为避免多个人来同时竞争同一个侧卫,在内部仍然使用锁来控制资源的同步访问。
下面直接写例子
业务需求:现在有20个人去售票厅窗口买票,但是窗口只有2个,那么同时能够买票 的只能有2个人,当2个人中的任意2个人买完票离开窗口之后,等待的18个任重又会有2个人可以占用窗口买票
真实需求:控制并发数为2
拆解转化业务需求:
1.人=线程
2.2个窗口=资源
3.在窗口买票=表示线程正在执行
4.离开窗口=线程执行完毕
5.等待买票=线程堵塞,等待执行
解决方案: 信号量 Semaphore
代码如下
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
/**
* Created with IntelliJ IDEA.
* User: tonfu.chia
* Date: 2018/3/7
* Time: 上午9:20
* To change this template use File | Settings | File Templates.
* <p>
* Description: 信号量 Semaphore
*/
public class SemaphoreDemo {
class MyTask implements Runnable {
//信号量
private Semaphore semaphore;
//买票用户
private int user;
public MyTask(Semaphore semaphore, int user) {
this.semaphore = semaphore;
this.user = user;
}
@Override
public void run() {
try {
//获取信号量许可,占用窗口
semaphore.acquire();
//运行到这里说明获取许可,可以买票
System.out.println("用户" + user + "进入窗口,准备买票.........");
//模拟买票所用时间
Thread.sleep((long) (Math.random() * 10000));
System.out.println("用户" + user + "买票完成,准备离开.........");
//模拟离开所用时间
Thread.sleep((long) (Math.random() * 10000));
System.out.println("用户" + user + "离开窗口.........");
//释放信号量许可
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void execute() {
//定义窗口个数
final Semaphore s = new Semaphore(2);
//执行线程
ExecutorService threadPool = Executors.newCachedThreadPool();
//模拟20个用户
for (int i = 0; i < 20; i++){
threadPool.execute(new MyTask(s,(i+1)));
}
threadPool.shutdown();
}
public static void main(String[] args) {
SemaphoreDemo demo=new SemaphoreDemo();
demo.execute();
}
}
执行结果
用户1进入窗口,准备买票.........
用户2进入窗口,准备买票.........
用户2买票完成,准备离开.........
用户2离开窗口.........
用户3进入窗口,准备买票.........
用户1买票完成,准备离开.........
用户3买票完成,准备离开.........
用户3离开窗口.........
用户4进入窗口,准备买票.........
用户1离开窗口.........
用户5进入窗口,准备买票.........
用户4买票完成,准备离开.........
现在常用的还是使用线程池来控制线程执行,和线程队列等待,很少用控制线程让其休眠再唤醒去控制并发
有成熟的就不要自己再写一个还有可能存在潜在风险的轮子了