線程間協做-《thinking in java》讀書筆記(一)

概述

線程間協做即有多個線程須要按照必定順序相互協做進行。主要有兩種方法來實現,使用鎖(互斥)來同步兩個任務的行爲。另外一種是使用BlockingQueue,它已經幫咱們處理好了同步機制,實現更加簡單。git

舉例

接下來以一個實際場景爲例,進行演示。假設在一個餐館中有一個服務員,有一個廚師,而服務員要等到廚子把菜作好了才能上菜,而後回來繼續等待。而廚師獲得新訂單後開始作菜。用兩種方式實現以前,咱們分析知廚師和服務員分別是一個獨立的線程,他們經過餐廳聯結在一塊兒。在這個模型中廚師表明生產者,服務員表明消費者。Order是他們共享的資源,須要進行同步。github

使用鎖的互斥

  • bash

    public class Order {
      private int num=0;
    
      public Order(int num) {
          this.num=num;
      }
    
      @Override
      public String toString() {
          return "order num:"+num;
      }
    }複製代碼
  • 餐館ide

    public class Restaurant {
     Order order;
     Chef chef=new Chef(this);
     Waiter waiter=new Waiter(this);
     ExecutorService executorService= Executors.newCachedThreadPool();
    
     public Restaurant() {
         order =null;
         executorService.execute(chef);
         executorService.execute(waiter);
         try {
             TimeUnit.SECONDS.sleep(5);
         }catch (Exception e){
             e.printStackTrace();
         }
         executorService.shutdown();
     }
     public static void main(String[] args){
         new Restaurant();
     }
    }複製代碼
  • 廚師
    public class Chef implements Runnable {
     private Restaurant restaurant;
     private int counter=0;
     public Chef(Restaurant restaurant) {
         this.restaurant = restaurant;
     }
     @Override
     public void run() {
         try{
             while (!Thread.interrupted()){
                 synchronized (this){
                     while (restaurant.meal!=null){
                         wait();//等服務員上菜,得到新訂單
                     }
                 }
                 synchronized (restaurant.waiter){
                     //得到服務員的鎖,讓他等我作菜
                     restaurant.meal=new Meal(counter++);
                     System.out.print("a meal is done");
                     Thread.sleep(500);
                     restaurant.waiter.notifyAll();
                     //告訴服務員能夠上菜了
                 }
             }
         }catch (Exception e){
             e.printStackTrace();
         }
     }
    }複製代碼
  • 服務員ui

    public class Waiter implements Runnable {
      private Restaurant restaurant;
    
      public Waiter(Restaurant restaurant) {
          this.restaurant = restaurant;
      }
    
      @Override
      public void run() {
          try {
              while (!Thread.interrupted()){
                  synchronized (this){
                      while (restaurant.order ==null){
                          wait();//等待廚師作完菜後被chef的notifyAll()喚醒,注意wait()會釋放當前得到的鎖
                      }
                  }
                  synchronized (restaurant.chef){
                      System.out.print("waiter: order up\n");
                      restaurant.order =null;
                      restaurant.chef.notifyAll();//告訴廚師能夠作菜了
                  }
              }
          }catch (Exception e){
              e.printStackTrace();
          }
      }
    }複製代碼

    圖片.png
    圖片.png

    因而可知chef與waiter按照順序協調了

使用BlockingQueue同步

  • 建立本身的BlockingQueue
    public class MealQueue extends LinkedBlockingQueue<Order> {
    }複製代碼
  • 餐館this

    public class Restaurant {
      private MealQueue waitQueue;
      private MealQueue finishedQueue;
      private Chef chef;
      private Waiter waiter;
    
      public Restaurant() {
          waitQueue = new MealQueue();
          finishedQueue = new MealQueue();
          chef = new Chef(waitQueue, finishedQueue);
          waiter = new Waiter(waitQueue, finishedQueue);
          ExecutorService executorService = Executors.newCachedThreadPool();
          executorService.execute(chef);
          executorService.execute(waiter);
    
          try {
              Thread.sleep(5000);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
    
          executorService.shutdown();
      }
    
      public static void main(String[] args){
          new Restaurant();
      }
    }複製代碼
  • 廚師spa

    public class Chef implements Runnable{
      private MealQueue waitQueue;
      private MealQueue finishedQueue;
    
      public Chef(MealQueue waitQueue, MealQueue finishedQueue) {
          this.waitQueue = waitQueue;
          this.finishedQueue = finishedQueue;
      }
    
      @Override
      public void run() {
          try {
              while (!Thread.interrupted()){
                  Order order =waitQueue.take();
                  Thread.sleep(500);
                  System.out.print("chef:order done "+ order.toString()+"\n");
                  finishedQueue.add(order);
              }
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
    }複製代碼
  • 服務員線程

    public class Waiter implements Runnable{
      private MealQueue waitQueue;
      private MealQueue finishedQueue;
      private int count;
    
      public Waiter(MealQueue waitQueue, MealQueue finishedQueue) {
          this.waitQueue = waitQueue;
          this.finishedQueue = finishedQueue;
          count=0;
      }
    
      @Override
      public void run() {
          try {
              while (!Thread.interrupted()){
                  Order newOrder=new Order(count++);
                  waitQueue.add(newOrder);
                  System.out.print("waiter:a new order\n");
                  Order order =finishedQueue.take();
                  System.out.print("waiter:order complete "+ order.toString()+"\n");
              }
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
    }複製代碼

圖片.png
圖片.png

在這個版本中,咱們沒有在任何一個地方顯示加鎖,但它仍能有序進行很是簡單

總結

咱們經過兩種方法完成了線程的協做,我的以爲使用BlockingQueuer更容易也更好管理。最後還有一個例子模擬生產吐司麪包,第一步製做吐司,第二步抹黃油,第三步塗果醬。代碼已同步到github,再也不贅述。如發現錯誤,歡迎指正。rest

相關文章
相關標籤/搜索