在学习操作系统的进程的同步操作中,老师有道实验题,要求使用信号量完成父亲女儿吃水果问题。基于C系语言对我的不友好,我询问了老师能否使用Java实现(因为Java里边好多方法都是现成的,不用自己写~~),距离学习Java线程已经过了快一年,刚好复习复习。
【问题描述】桌上有一空盘,最多允许存放一个水果。爸爸可向盘中放一个苹果或放一个桔子,儿子专等吃盘中的桔子,女儿专等吃苹果。 试用P、V操作实现爸爸、儿子、女儿三个并发进程的同步。 提示:设置一个信号量表示可否向盘中放水果,一个信号量表示可否取桔子,一个信号量表示可否取苹果。
问题分析
- 桌子上只有一个盘子,父亲每次只能放入一种水果,同一时间内,盘子里只能有一种水果
- 儿子和女儿从盘子里拿水果,水果只有一个,同一时间内只有一个人吃水果,所以儿子和女儿也是互斥关系
- 父亲放何种水果制约了女儿和儿子取何种水果
Java中的Semaphore
//此信号量获得一个许可,在提供一个许可前将线程堵塞,否则线程被中断。获得一个许可并立即返回,将许可数减一
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
//释放一个许可,将其返回给信号量。将可用的许可数加一
public void release() {
sync.releaseShared(1);
}
也就是说,在初始化一个信号量的时候,例如构造函数: Semaphore Semaphore = new Semaphore(1); 信号量的许可数为1,每acquire一次,许可数就 -1,每release一次,许可数就+1,当许可数变为0还acquire的话,该线程会进入阻塞状态,直到release方法将许可数置为1,才能执行
实例代码
package thread;
import java.util.concurrent.Semaphore;
/**
* @ClassName App
* @Author hobo
* @Date 19-4-17 下午3:37
* @Description
**/
public class App {
//初始化桌子为空的信号量为1 使父亲线程能够运行 1 桌子为空,0 桌子不为空
public static Semaphore diskEmpty = new Semaphore(1);
//初始化信号量为0,使得女儿和儿子的进程阻塞
public static Semaphore haveOrange = new Semaphore(0);
public static Semaphore haveApple = new Semaphore(0);
public static void main(String[] args) {
Father father = new Father();
Son son = new Son();
Daughter daughter = new Daughter();
father.start();
son.start();
daughter.start();
}
}
父亲线程
package thread;
import java.util.Random;
/**
* @ClassName Father
* @Author hobo
* @Date 19-4-17 下午3:36
* @Description
**/
public class Father extends Thread {
@Override
public void run() {
try {
while (true) {
//使用随机数控制父亲放入橘子还是苹果
Random random = new Random();
int n = random.nextInt(100);
App.diskEmpty.acquire();
if (n % 2 == 0) {
Disk.putFruit("橘子");
App.haveOrange.release();
} else {
Disk.putFruit("苹果");
App.haveApple.release();
}
sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
儿子线程
package thread;
/**
* @ClassName Son
* @Author hobo
* @Date 19-4-17 下午3:36
* @Description
**/
public class Son extends Thread {
private static String name = "儿子";
@Override
public void run() {
try {
while (true) {
App.haveOrange.acquire();
Disk.getFruit(name);
sleep(1000);
App.diskEmpty.release();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
女儿线程
package thread;
/**
* @ClassName Daughter
* @Author hobo
* @Date 19-4-17 下午3:37
* @Description
**/
public class Daughter extends Thread {
private static String name = "女儿";
@Override
public void run() {
try {
while (true) {
App.haveApple.acquire();
Disk.getFruit(name);
sleep(1000);
App.diskEmpty.release();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
盘子
package thread;
/**
* @ClassName Disk
* @Author hobo
* @Date 19-4-17 下午3:37
* @Description
**/
public class Disk {
private static String fruitName = "";
public static void putFruit(String fruit) {
fruitName = fruit;
System.out.println("父亲往盘子里边放了一个" + fruit);
}
public static String getFruit(String name) {
System.out.println(name + "吃了一个" + fruitName);
return fruitName;
}
}
运行结果
Github传送门