程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java開發多線程同步技巧

Java開發多線程同步技巧

編輯:關於JAVA

在編寫一個類時,如果該類中的代碼可能運行於多線程環境下,那麼就要考慮同步的問題。在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的值,所以,同時多個線程對它訪問不會造成數據的混亂。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved