1、生产者——消费者问题
(1)Semaphore实现
public class ProducerConsumer {
int count = 0;// 缓冲区的产品数量
Semaphore mutex = new Semaphore(1);
Semaphore empty = new Semaphore(N);
Semaphore full = new Semaphore(0);
class Producer implements Runnable {
public void run() {
while (true) {
Item item = produce();
empty.acquire();
mutex.acquire();
insert(item);// 向缓冲区添加产品
count++;
mutex.release();
full.release();
}
}
public Item produce() {...}
}
class Consumer implements Runnable {
public void run() {
while (true) {
full.acquire();
mutex.acquire();
Item item = remove();// 从缓冲区取出数据
count--;
mutex.release();
empty.release();
consume(item);
}
}
public void consume(Item item) {...}
}
}
(2)synchronized和wait()/notifyAll()实现
class Resource {
// 当前资源数量
private int num = 0;
// 资源池中允许存放的资源数目
private int size = 10;
/**
* 从资源池中取走资源
*/
public synchronized void remove() {
if (num > 0) {
num--;
notifyAll();// 通知生产者生产资源
} else {
try {
// 如果没有资源,则消费者进入等待状态
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 向资源池中添加资源
*/
public synchronized void add() {
if (num < size) {
num++;
// 通知等待的消费者
notifyAll();
} else {
// 如果当前资源池中有10件资源
try {
wait();// 生产者进入等待状态,并释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
然后建立Class Producer和Consumer implements Runnable,有参数为Resource的构造器,在run()方法里循环调用resource.add()/remove()。
(3)lock和conditon.await()/signalAll()实现
Lock lock = new ReentrantLock();
Condition producerCondition = lock.newCondition();
Condition consumerCondition = lock.newCondition();
和synchronized的实现类似,只不过用lock.lock()/unlock()代替同步块,condition的await()/signalAll()代替wait()/notifyAll()。
(4)BlockingQueue的实现
put(),take()即可。
2、哲学家就餐(假设有5个哲学家)
(1)synchronized和wait()/notifyAll()实现
class Fork {
private boolean[] used = new boolean[5];
// i表示哲学家的编号
public synchronized void takeFork(int i) {
// wait()要放在while里,防止notifyAll()唤醒了不该唤醒的线程
while (used[i] && used[(i + 1) % 5]) {
wait();
}
used[i] = true;
used[(i + 1) % 5] = true;
}
public synchronized void putFork(int i) {
used[i] = false;
used[(i + 1) % 5] = false;
notifyAll();
}
}
(2)Semaphore实现
class Fork {
boolean[] isEating = new boolean[5];
Semaphore mutex = new Semaphore(1);
// 哲学家的信号量
Semaphore[] s = new Semaphore[5];
// i是哲学家的编号
public void takeForks(int i) {
mutex.acquire();
test(i);
mutex.release();
// i得不到forks则阻塞
s[i].acquire();
}
public void putForks(int i) {
mutex.acquire();
isEating[i] = false;
// 测试左右的人是否可以拿起筷子
test((i + 1) % 5);
test((i + 4) % 5);
mutex.release();
}
public void test(int i) {
if (!isEating[i] && !isEating[(i + 1) % 5] && !isEating[(i + 4) % 5]) {
isEating[i] = true;
s[i].release();
}
}
}
还有随机回退、给筷子编号,按编号取筷子等方法。
3、银行家算法,找出安全的操作序列,可以预防死锁。检测死锁可以维护资源分配表和进程等待表(进程正在等待的资源),判断是否有环。
假设有m种资源,n个进程,need[n][m]是各进程需要的资源,allocated[n][m]是各进程已分配的资源,isFinished[n]表示进程是否结束,available[m]表示可用的资源
int i = 0;
current = 0;
while(i < n){
for(int j = 0; j < m; j ++) {
if(isFinished[i] || need[i][j] > available[j]) {
i ++;
break;
}
if(j == m - 1) {
for(int k = 0; k < m; k ++)
available[k] +=allocated[i][k];
isFinished[i] = true;
// 安全的执行序列
queue[current ++] = i;
i = 0;
}
}
}
// 判断是否有安全序列
for(int i = 0; i < n; i ++) {
if(!isFinished[i])
return false;
}
return true;
复杂度高,O(mn2)