进程同步控制
1,问题描述
问题描述:编写Windows下父亲儿子女儿放取水果进程同步的演示程序。(问题描述:桌上有一空盘,最多允许存放一个水果。爸爸可向盘中放一个苹果或放一个桔子,儿子专等吃盘中的桔子,女儿专等吃苹果。 试用P、V操作实现爸爸、儿子、女儿三个并发进程的同步。提示:设置一个信号量表示可否向盘中放水果,一个信号量表示可否取桔子,一个信号量表示可否取苹果。
在解决进程同步问题中,有一种方式叫做信号量机制,即通过信号量来将不同的进程进行控制和使用,使其避免出现死锁现象。
在本题中,父亲放水果,儿子拿苹果,女儿拿香蕉都是不同的进程,可以在不同的时间允许和操作。但如何进行,这就是要解决的问题。
2,Java中的信号量是java.util.concurrent.Semaphore
函数实现的。
信号量
Semaphore
通过使用计数器counter
来控制对共享资源的访问。如果计数器大于零,则允许访问。如果为零,则拒绝访问。计数器对共享资源的访问许可进行计数。因此,要访问资源,线程必须要从信号量得到许可。
Semaphore 的构造函数有两种:
Semaphore(int num)
Semaphore(int num, boolean how)
其中的
num
,表示指定初始的许可计数。因此,它也就指定了一次可以同时访问共享资源的线程数。如果是1
,那么同时只能有一个线程可以访问该资源。默认情况下,所有等待的线程都以未定义的顺序被授予许可
。通过设置how为true
,可以确保等待线程按其请求访问的顺序被授予许可
。
该函数会使用到的方法有,Semaphore.acquire()
,获取信号量的控制权的锁,类比于P
操作,Semaphore.release()
,释放信号量的控制锁,类比于V
操作。
关于信号量的博客:Java中的信号量Semaphore
下面开始使用,这本题中设置,3个信号量:
static Semaphore empty = new Semaphore(1,true);//资源区是否为空
static Semaphore apple = new Semaphore(0,true);//资源区苹果信号
static Semaphore banana = new Semaphore(0,true);//资源区香蕉信号
第一个empty
信号量表示,盘中是否有水果,也是对于父亲的放水果的操作的控制。apple
信号量,用于儿子的拿苹果的操作,banana
用于女儿拿香蕉的操作。
3,父亲的相关代码:
//父亲的进程
Thread father = new Thread(new Runnable() {
public void run() {
while (true) {
try {
empty.acquire();//申请操作权限相当于wait(S)
int random = Math.random() >= 0.5 ? 1 : 0;
if (random == 1) {
System.out.println("父亲放入了一个苹果");
Thread.sleep(1000);//休眠表示放入的过程
apple.release();//唤醒儿子的访问
} else {
System.out.println("父亲放入了一个香蕉");
Thread.sleep(1000);
banana.release();//唤醒女儿的访问
}
} catch (InterruptedException e) {
System.out.println("父亲获取资源失败!");
e.printStackTrace();
}
}
}
});
父亲先获取操作权,然后进行放水果的操作。放好以后,根据放的水果的种类,唤醒对于的进程。
4,女儿和儿子的代码:
//女儿的进程
Thread daughter = new Thread(new Runnable() {
public void run() {
while (true){
try {
banana.acquire();//获取banana的资源
System.out.println("女儿取走了一个香蕉!");
Thread.sleep(1000);//取走的过程
empty.release();//释放父亲放的信号量
} catch (InterruptedException e) {
System.out.println("女儿获取资源失败!");
e.printStackTrace();
}
}
}
});
//儿子的进程
Thread son = new Thread(new Runnable() {
public void run() {
while (true){
try {
apple.acquire();//儿子获取资源
System.out.println("儿子取走了一个苹果!");
Thread.sleep(1000);//取的过程
empty.release();//释放资源
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("儿子获取资源失败!");
}
}
}
});
儿子或女儿取了水果以后,由于只能存放一个,因此直接将控制权返回给父亲,即唤醒父亲的进程。
5,完整代码实例:
import java.util.concurrent.Semaphore;
/**
* 信号量Semaphore 通过使用计数器counter来控制对共享资源的访问。
* 如果计数器大于零,则允许访问。如果为零,则拒绝访问。
* 计数器对共享资源的访问许可进行计数。因此,要访问资源,线程必须要从信号量得到许可。
*/
public class ProcessTest {
static Semaphore empty = new Semaphore(1,true);//资源区是否为空
static Semaphore apple = new Semaphore(0,true);//资源区苹果信号
static Semaphore banana = new Semaphore(0,true);//资源区香蕉信号
public static void main(String[] args) {
//父亲的进程
Thread father = new Thread(new Runnable() {
public void run() {
while (true) {
try {
empty.acquire();//申请操作权限相当于wait(S)
int random = Math.random() >= 0.5 ? 1 : 0;
if (random == 1) {
System.out.println("父亲放入了一个苹果");
Thread.sleep(1000);//休眠表示放入的过程
apple.release();//唤醒儿子的访问
} else {
System.out.println("父亲放入了一个香蕉");
Thread.sleep(1000);
banana.release();//唤醒女儿的访问
}
} catch (InterruptedException e) {
System.out.println("父亲获取资源失败!");
e.printStackTrace();
}
}
}
});
//女儿的进程
Thread daughter = new Thread(new Runnable() {
public void run() {
while (true){
try {
banana.acquire();//获取banana的资源
System.out.println("女儿取走了一个香蕉!");
Thread.sleep(1000);//取走的过程
empty.release();//释放父亲放的信号量
} catch (InterruptedException e) {
System.out.println("女儿获取资源失败!");
e.printStackTrace();
}
}
}
});
Thread son = new Thread(new Runnable() {
public void run() {
while (true){
try {
apple.acquire();//儿子获取资源
System.out.println("儿子取走了一个苹果!");
Thread.sleep(1000);//取的过程
empty.release();//释放资源
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("儿子获取资源失败!");
}
}
}
});
father.setName("父亲");
daughter.setName("女儿");
son.setName("儿子");
father.start();
daughter.start();
son.start();
}
}