Java開發中多線程同步技巧

來源:互聯網
上載者:User
在編寫一個類時,如果該類中的代碼可能運行於多線程環境下,那麼就要考慮同步的問題。在Java中內建了語言級的同步原語--synchronized,這也大大簡化了Java中多線程同步的使用。

 我們首先編寫一個非常簡單的多線程的程式,是類比銀行中的多個線程同時對同一個儲蓄賬戶進行存款、取款操作的。

  在程式中我們使用了一個簡化版本的Account類,代表了一個銀行賬戶的資訊。在主程式中我們首先產生了1000個線程,然後啟動它們,每一個線程都對John的賬戶進行存100元,然後馬上又取出100元。這樣,對於John的賬戶來說,最終賬戶的餘額應該是還是1000元才對。然而啟動並執行結果卻超出我們的想像,首先來看看我們的示範代碼:

class Account
{
 String name; float amount;
 public Account(String name, float amount)
 {
  this.name = name;
  this.amount = amount;
 }
 
 public void deposit(float amt)
 {
  float tmp = amount;
  tmp += amt;
  try
  {
   Thread.sleep(100);
   //類比其它處理所需要的時間,比如重新整理資料庫等
  }
  catch (InterruptedException e)
  {
   // ignore
  }
  amount = tmp;
 }

 public void withdraw(float amt)
 {
  float tmp = amount;
  tmp -= amt;
  try
  {
   Thread.sleep(100);
   //類比其它處理所需要的時間,比如重新整理資料庫等
  }
  catch (InterruptedException e)
  {
   // ignore
  }
  amount = tmp;
 }

 public float getBalance()
 {
  return amount;
  }
}

public class AccountTest
{
 private static int NUM_OF_THREAD = 1000;
 static Thread[] threads = new Thread[NUM_OF_THREAD];
 public static void main(String[] args)
 {
  final Account acc = new Account("John", 1000.0f);
  for (int i = 0; i< NUM_OF_THREAD; i++)
  {
   threads[i] = new Thread(new Runnable()
   {
    public void run()
    {
     acc.deposit(100.0f);
     acc.withdraw(100.0f);
    }
   }
   );
  threads[i].start();
 }
 for (int i=0; i<NUM_OF_THREAD; i++)
 {
  try { threads[i].join();
  //等待所有線程運行結束
 }
 catch (InterruptedException e)
 {
  // ignore
 }
}
System.out.println("Finally, John's balance is:" + acc.getBalance()); }}

注意,上面在Account的deposit和withdraw方法中之所以要把對amount的運算使用一個臨時變數首先儲存,sleep一段時間,然後,再賦值給amount,是為了類比真實運行時的情況。因為在真實系統中,賬戶資訊肯定是儲存在持久媒介中,比如RDBMS中,此處的睡眠的時間相當於比較耗時的資料庫操作,最後把臨時變數tmp的值賦值給amount相當於把amount的改動寫入資料庫中。運行AccountTest,結果如下(每一次結果都會不同):

E:/java/exer/bin>java AccountTest
Finally, John's balance is:3900.0

E:/java/exer/bin>java AccountTest
Finally, John's balance is:4900.0

E:/java/exer/bin>java AccountTest
Finally, John's balance is:4700.0

E:/java/exer/bin>java AccountTest
Finally, John's balance is:3900.0

E:/java/exer/bin>java AccountTest
Finally, John's balance is:3900.0

E:/java/exer/bin>java AccountTest
Finally, John's balance is:5200.0

  為什麼會出現這樣的問題?這就是多線程中的同步的問題。在我們的程式中,Account中的amount會同時被多個線程所訪問,這就是一個競爭資源,通常稱作競態條件。對於這樣的多個線程共用的資源我們必須進行同步,以避免一個線程的改動被另一個線程所覆蓋。在我們這個程式中,Account中的amount是一個競態條件,所以所有對amount的修改訪問都要進行同步,我們將deposit()和withdraw()方法進行同步,修改為:

public synchronized void deposit(float amt)
{
 float tmp = amount;
 tmp += amt;
 try
 {
  Thread.sleep(1);
  //類比其它處理所需要的時間,比如重新整理資料庫等
 }
 catch (InterruptedException e)
 {
  // ignore
 }
 amount = tmp;
}

public synchronized void withdraw(float amt)
{
 float tmp = amount;
 tmp -= amt;
 try
 {
  Thread.sleep(1);
  //類比其它處理所需要的時間,比如重新整理資料庫等
 }
 catch (InterruptedException e)
 { // ignore }
  amount = tmp;
}

  此時,再運行,我們就能夠得到正確的結果了。Account中的getBalance()也訪問了amount,為什麼不對getBalance()同步呢?因為getBalance()並不會修改amount的值,所以,同時多個線程對它訪問不會造成資料的混亂。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.