Java 信号量机制实现
Java 信号量机制实现
信号量 Semaphore
信号量可限制访问共享资源的线程数。线程访问资源前需从信号量获取许可,访问结束后需将许可归还信号量。

场景介绍
一个停车场只有5个车位,现有100辆车争抢这5个车位。理想情况下,最多同时有5辆车能抢到车位,未抢到车位的车需等待,直到其他车让出车位后才有机会使用。
实现代码
package com.semaphore;
import java.util.concurrent.*;
/**
* @author Jing61
*/
public class CarPark {
public static void main(String[] args) {
//阻塞队列
BlockingQueue<String> parks = new LinkedBlockingQueue<>(5);
parks.offer("车位一");
parks.offer("车位二");
parks.offer("车位三");
parks.offer("车位四");
parks.offer("车位五");
ExecutorService executorService = Executors.newCachedThreadPool();
// 5个车位(许可)
Semaphore semaphore = new Semaphore(5);
for (int i = 0; i < 100; i++) {
final int no = i;
Thread t1 = new Thread(() -> {
try {
/**
* 获取许可,首先判断semaphore内部的数字是否大于0,如果大于0,
* 才能获得许可,然后将初始值5减去1,线程才会接着去执行;如果没有
* 获得许可(原因是因为已经有5个线程获得到许可,semaphore内部的数字为0),
* 线程会阻塞直到已经获得到许可的线程,调用release()方法,释放掉许可,
* 也就是将semaphore内部的数字加1,该线程才有可能获得许可。
*/
semaphore.acquire();
/**
* 对应的线程会到阻塞对,对应车辆去获取到车位,如果没有拿到一致阻塞,
* 直到其他车辆归还车位。
*/
String park = parks.take();
System.out.println("车辆【" + no + "】获取到: " + park);
Thread.sleep((long) Math.random() * 6000);
System.out.println("车辆【" + no + "】离开 " + park);
semaphore.release(); //线程释放掉许可,通俗来将就是将semaphore内部的数字加1
parks.offer(park); //归还车位
} catch (InterruptedException e) {
e.printStackTrace();
}
});
executorService.execute(t1);
}
executorService.shutdown();
}
}
哲学家进餐问题
1965年,荷兰计算机科学家、图灵奖得主Edsger Wybe Dijkstra提出并解决了“哲学家进餐”同步问题。
问题描述
五个哲学家围坐在圆桌旁,每人面前有一盘通心粉。由于通心粉较滑,需两把叉子才能夹住。相邻两个盘子之间放有一把叉子(如下图所示)。

哲学家的生活有两种交替活动:吃饭和思考。当哲学家感到饥饿时,会尝试分两次取左右两边的叉子(每次拿一把,不分次序)。成功拿到两把叉子后开始吃饭,吃完后放下叉子继续思考。
若将哲学家替换为线程,叉子替换为竞争的临界资源,该问题即为线程竞争资源问题。若设计不当,系统可能出现死锁、活锁、吞吐量下降等问题。
信号量原语解决方案(Java 实现)
以下是使用 Java 5 并发工具包中的 Semaphore 类解决哲学家进餐问题的代码:
package com.wise.tiger;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
/**
* 存放线程共享信号量上下文
* @author Jing61
*/
public class SemaphoreContext {
public static final int NUM_OF_FORKS = 5;//叉子数量(资源)
public static final int NUM_OF_PHILO = 5;//哲学家数量(线程)
public static Semaphore[] forks;
public static Semaphore counter;
static{
forks = new Semaphore[NUM_OF_FORKS];
for(int i = 0 ; i < forks.length; i++){
forks[i] = new Semaphore(1);
}
counter = new Semaphore(NUM_OF_PHILO - 1);
}
public static void putFork(int index,boolean leftFirst) throws Exception{
if(leftFirst) {
forks[index].acquire();
forks[(index + 1) % NUM_OF_PHILO].acquire();
}
else {
forks[(index + 1) % NUM_OF_PHILO].acquire();
forks[index].acquire();
}
}
public static void takeFork(int index,boolean leftFirst) throws Exception{
if(leftFirst) {
forks[index].release();
forks[(index + 1) % NUM_OF_PHILO].release();
}
else {
forks[(index + 1) % NUM_OF_PHILO].release();
forks[index].release();
}
}
static class Philo implements Runnable{
private int index;
private String name;
public Philo(int index, String name) {
this.index = index;
this.name = name;
}
@Override
public void run() {
while (true) {
try {
counter.acquire();
boolean isLeftFirst = index % 2 == 0;
putFork(index, isLeftFirst);
System.out.println(name + "正在吃通心粉。。。。。");
takeFork(index, isLeftFirst);
System.out.println(name + "吃完了,正在思考");
counter.release();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Philo[] philos = {new Philo(0,"peppa"),
new Philo(1,"pedro"),
new Philo(2,"emily"),
new Philo(3,"suzy"),
new Philo(4,"danny"),};
var pool = Executors.newCachedThreadPool();
for(int i= 0; i < philos.length;i++){
pool.execute(philos[i]);
}
pool.shutdown();
}
}

浙公网安备 33010602011771号