1. 程式人生 > >Java多執行緒4—執行緒同步問題+火車票售票系統

Java多執行緒4—執行緒同步問題+火車票售票系統

        在上一篇文章中寫到了許多執行緒共享同一資料,這種情況在現實的生活中也是經常發生的,比如火車站的火車票售票系統。火車票售票系統是一個常年執行的系統,為了滿足乘客的需求,我們不能只設一個視窗,必須設很多的售票視窗,每個售票視窗就像一個執行緒,它們各自執行,共同訪問相同的資料——火車票的數量,下面我們用多執行緒模仿一下火車票售票系統:

public

class TicketSystem

{

   public static void main(String[] args)

   {

      SellThread st=new SellThread();

      new

Thread(st).start();new Thread(st).start();new Thread(st).start();

   }

}

class

SellThread implements Runnable

{

   int tickets=100;

   public void run()

   {

      while(true)

      {

         if(tickets>0)

         {

                System.out.println("obj:"+Thread.currentThread

().getName()+" sell tickets:"+tickets);

                tickets--;

         }

      }

   }

}

         程式輸出了執行緒0、1、2三個執行緒從第100張票賣到第1張票的過程,這個程式正確的輸出了。但是實際情況中火車票售票系統是常年執行的,有可能第一顧客已經買到了最後一張票,但是執行到tickets--的時候,執行緒切換了,這時候另外一個顧客也買最後一張票,這時候系統顯示還有票,這時候這個顧客也訂票了,並且也在執行到tickets--的時候,執行緒切換。再回去執行原來的執行緒,前個顧客買到了最後一張票,後一個顧客但是卻買到了第0張票。顯然這個是不對的。下面我們修改上面程式模擬一下:

class

SellThread implements Runnable

{

   int tickets=100;

   public void run()

   {

      while(true)

      {

         if(tickets>0)

         {

            Try//讓執行緒睡眠10毫秒,只是修改了這裡

            {

                Thread.sleep(10);

            } catch (InterruptedExceptione)

            {

                e.printStackTrace();

            }

            System.out.println("obj:"+Thread.currentThread().getName()+" sell tickets:"+tickets);

            tickets--;

         }

      }

   }

}

結果的一部分如下:

obj:Thread-0 sell tickets:2

obj:Thread-2 sell tickets:2

obj:Thread-1 sell tickets:0

obj:Thread-0 sell tickets:-1

        我們可以看出上面程式兩次售出了第二張票,並且賣出了第0張和-1張票,顯然這個結果是不對的。遇到了不對的情況,我們就應該得想辦法解決它,在Java中運用同步的方法解決這個問題。

        在介紹之前我們先說一下臨界區,在Java中程式碼段訪問了同一個物件或資料,那麼這個程式碼段就叫做臨界區。上面程式中run()方法中迴圈裡面的程式碼就叫做臨界區。我們需要對這個臨界區進行保護,這樣就用到了執行緒的同步。

       在Java中執行緒的同步有兩種方法,一種是同步塊,另一種是同步方法。不管是哪種方法都用到了synchronized關鍵字。下面我們就修改上面的程式用例項顯示一下:

                  class SellThread implementsRunnable

{

      int tickets=100;

      Object obj = new Object();

      public void run()

      {

         while(true)

         {

            synchronized(obj)//加上一個同步關鍵字

            {

               if(tickets>0)

               {

                  try{

                     Thread.sleep(10);

                  } catch (InterruptedExceptione)

                  {

                     e.printStackTrace();

                  }

System.out.println("obj:"+Thread.currentThread().getName()+" sell tickets:"+tickets);

                  tickets--;

                  }

               }

            }

         }

}

        上面使用synchronized關鍵字的時候需要一個物件,這裡的物件可以是任意物件,我們在這裡選擇的是Object物件,你也可以選擇String類的物件。每一個物件都有一個監視器或者叫鎖。當我將obj作為引數傳給synchronized關鍵字的時候,就相當於給這段程式碼加了一個鎖,當我執行到這段程式碼的時候,先判斷是否加鎖,如果沒有加鎖,先將其加鎖然後執行程式碼。如果加鎖了只能等待。等到給程式碼加鎖的執行緒執行完成之後,會將鎖開啟。

下面我們介紹同步方法,我們還是以程式的形式給出:

         class SellThread implements Runnable

         {

            int tickets=100;

            Object obj = new Object();

            public synchronized void sell()//用關鍵字sychronized                                 //關鍵字標誌同步方法

            {

                  if(tickets>0)

                  {

                     try

                     {

                        Thread.sleep(10);

                     }

                     catch(Exception e)

                     {

                        e.printStackTrace();

                     }

   System.out.println("sell():"+Thread.currentThread().getName()+

                         " sell tickets:"+tickets);

                        tickets--;

                  }

               }

         public void run()

         {

            while(true)

            {

               sell();

            }

         }

}

        上面的程式用sychronized關鍵字直接標誌的是sell()方法,我們在run()方法裡呼叫sell()的時候,sell()方法中的程式碼就相當於一個整體都一起執行。但是同步方法也是使用加鎖的方法進行同步的,它不像同步塊那樣傳遞一個物件,那麼他是對哪個物件加的鎖呢?同步方法是對程式中的this物件加鎖以實現同步的。

我們有時候會寫一些靜態的方法,這些方法也必須同步。靜態方法只屬於類本身,沒有this物件,那麼它對誰加鎖呢?前面我們介紹過每一個類都有一個Class類的物件(詳細介紹請看:http://blog.csdn.net/mengxiangyue/article/details/6831820),靜態方法正是對這個Class類的物件進行加鎖以實現同步方法的。

        對於同步方法就先介紹到這裡,如果有錯請大家指出,希望對大家有幫助。