在學習操作系統的進程的同步操作中,老師有道實驗題,要求使用信號量完成父親女兒喫水果問題。基於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傳送門