Java信号量实现进程同步问题:水果苹果香蕉问题

进程同步控制

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

        猜你喜欢

        转载自blog.csdn.net/hhb442/article/details/109106732