共享锁,用于类似多个窗口排队买票的场景,可以同时进行多个线程的处理,超出的入队等待
实现:
/*
业务场景:
现在又20个人排队买票,但是窗口只有三个,一个窗口一次只能让一个线程买票
*/
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 20; i++) {
new Thread(()->{
try {
// 加锁
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "正在买票");
Thread.sleep(5000L);
System.out.println(Thread.currentThread().getName() + "买票完成");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
// 释放
semaphore.release();
}
}).start();
}
输出:
Thread-0正在买票
Thread-1正在买票
Thread-2正在买票
5S:
Thread-1买票完成
Thread-2买票完成
Thread-3正在买票
Thread-0买票完成
Thread-4正在买票
Thread-5正在买票
...
上面例子中给sleep添加一个条件可以看出一个线程释放了立马就会有一个线程就执行
if(!Thread.currentThread().getName().equals("Thread-0")){
Thread.sleep(5000L);
}
原理
默认实现的非公平的实现方式
同时也能传入参数自行调整公平与非公平
初始化时会给state赋值,值为传入的参数,设置信号量
加锁过程:
semaphore.acquire();
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// tryAcquireShared中尝试加锁
if (tryAcquireShared(arg) < 0)
// 没有拿到锁就入队
doAcquireSharedInterruptibly(arg);
}
// 获取资源的逻辑
// acquires == 1
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
// 拿到现在的state
int available = getState();
// 减去传入的间隔
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining)) // CAS修改
return remaining;
}
}
Semaphore在解锁的时候哟传播的特性,当先线程唤醒了修改了state的值去执行,他会判断当下线程的下一个节点的waitState是不是-1(-1代表可以被唤醒),要是等于-1就唤醒下一个线程去尝试竞争资源,不行就休眠