Java訊號量實現程式同步問題:水果蘋果香蕉問題

Peanutty發表於2020-11-04

程式同步控制

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();
            }
        
        }
        
        

        相關文章