來源:互聯網網民 2008-05-31 12:10:19
評論摘要
開發者有時創建的多線程程序會生成錯誤值或産生其它希奇的行爲。古怪行爲一般出現在一個多線程程序沒使用同步連載線程訪問要害代碼部份的時候。同步連載線程訪問要害代碼部份是什麽意思呢?在這篇文章中解釋了同步,Java的同步機制,以及當開發者沒有正確使用這個機制時出現的兩個問題。一旦你看完這篇文章,你就可以避免在你的多線程Java程序中因缺乏同步而産生的希奇行爲。
創建多線程Java程序難嗎?僅從《用Java線程獲取優異性能(I)》中獲得的信息你就可以回答,不。究竟,我已經向你顯示了如何輕松地創建線程對象,通過調用Thread的start()方法起動與這些對象相關的線程,以及通過調用其它Thread方法,比如三個重載的join()方法執行簡單的線程操作。至今仍有許多開發者在開發一些多線程程序時面臨困難境遇。他們的程序經常功能不穩定或産生錯誤值。例如,一個多線程程序可能將不正確的雇員資料存貯在數據庫中,比如姓名和地址。姓名可能屬于一個雇員的,而地址卻屬于另一個的。是什麽引起這種希奇行爲的呢? 是缺乏同步:連載行爲,或在同一時間排序,線程訪問那些讓多重線程操作的類和字段變量實例的代碼序列,以及其他共享資源。我稱這些代碼序列爲要害代碼部份。
注重:不象類和實例字段變量,線程不能共享本地變量和參數。原因是:本地變量和參數在一個線程方法中分配??叫堆棧。結果,每一個線程都收到它自己對那些變量的拷貝。相反,線程能夠共享類字段和實例字段因爲那些變量在一個線程方法(叫堆棧)中沒有被分配。取而代之,它們作爲類(類字段)或對象(實例字段)的一部份在共享內存堆中被分配。
這篇文章將教你如何使用同步連載線程訪問要害代碼部份。我用一個說明爲什麽一些多線程程序必須使用同步的例子作爲開始。我接下來就監視器和鎖探討Java的同步機制和synchronized 要害字。我通過研究由這樣的錯用産生的兩個問題判定經常因爲不正確的使用同步機制而否認了它的好處。
閱讀關于線程程序的整個系列:
? 第I部份:介紹線程、線程類及Runnable
? 第II部份:使用同步連載線程訪問要害代碼部份
對于同步的需要
爲什麽我們需要同步呢?一種回答,考慮這個例子:你寫一個使用一對線程模擬取款/存款金融事務的Java程序。在那個程序中,一個線程處理存款,同時其它線程正處理取款。每一個線程操作一對共享變量、類及實例字段變量,這些用來標識金融事務的姓名和賬號。對于一個正確的金融事務,每一個線程必須在其它線程開始給name和amount賦值前(並且同時打印那些值)給name和amount變量賦值(並打印那些值,模擬存貯事務)。其源代碼如下:
列表1. NeedForSynchronizationDemo.java
// NeedForSynchronizationDemo.java
class NeedForSynchronizationDemo
{
public static void main (String [] args)
{
FinTrans ft = new FinTrans ();
TransThread tt1 = new TransThread (ft, "Deposit Thread");
TransThread tt2 = new TransThread (ft, "Withdrawal Thread");
tt1.start ();
tt2.start ();
}
}
class FinTrans
{
public static String transName;
public static double amount;
}
class TransThread extends Thread
{
private FinTrans ft;
TransThread (FinTrans ft, String name)
{
super (name); //保存線程名稱
this.ft = ft; //保存對金融事務對象的引用
}
public void run ()
{
for (int i = 0; i
{
if (getName ().equals ("Deposit Thread"))
{
//存款線程要害代碼部份的開始
ft.transName = "Deposit";
try
{
Thread.sleep ((int) (Math.random () * 1000));
}
catch (InterruptedException e)
{
}
ft.amount = 2000.0;
System.out.println (ft.transName + " " + ft.amount);
//存款線程要害代碼部份的結束
}
else
{
//取款線程要害代碼部份的開始
ft.transName = "Withdrawal";
try
{
Thread.sleep ((int) (Math.random () * 1000));
}
catch (InterruptedException e)
{
}
ft.amount = 250.0;
System.out.println (ft.transName + " " + ft.amount);
//取款線程要害代碼部份的結束
}
}
}
}
NeedForSynchronizationDemo的源代碼有兩個要害代碼部份:一個可理解爲存款線程,另一個可理解爲取款線程。在存款線程要害代碼部份中,線程分配Deposit String對象的引用給共享變量transName及分配2000.0 給共享變量amount。同樣,在取款要害代碼部份,線程分配Withdrawal String對象的引用給transName及分配250.0給amount。在每個線程的分配之後打印那些變量的內容。當你運行NeedForSynchronizationDemo時,你可能期望輸出類似于Withdrawal 250.0 和Deposit 2000.0兩行組成的列表。
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
摘要
開發者有時創建的多線程程序會生成錯誤值或産生其它希奇的行爲。古怪行爲一般出現在一個多線程程序沒使用同步連載線程訪問要害代碼部份的時候。同步連載線程訪問要害代碼部份是什麽意思呢?在這篇文章中解釋了同步,Java的同步機制,以及當開發者沒有正確使用這個機制時出現的兩個問題。一旦你看完這篇文章,你就可以避免在你的多線程Java程序中因缺乏同步而産生的希奇行爲。
創建多線程Java程序難嗎?僅從《用Java線程獲取優異性能(I)》中獲得的信息你就可以回答,不。究竟,我已經向你顯示了如何輕松地創建線程對象,通過調用Thread的start()方法起動與這些對象相關的線程,以及通過調用其它Thread方法,比如三個重載的join()方法執行簡單的線程操作。至今仍有許多開發者在開發一些多線程程序時面臨困難境遇。他們的程序經常功能不穩定或産生錯誤值。例如,一個多線程程序可能將不正確的雇員資料存貯在數據庫中,比如姓名和地址。姓名可能屬于一個雇員的,而地址卻屬于另一個的。是什麽引起這種希奇行爲的呢? 是缺乏同步:連載行爲,或在同一時間排序,線程訪問那些讓多重線程操作的類和字段變量實例的代碼序列,以及其他共享資源。我稱這些代碼序列爲要害代碼部份。
注重:不象類和實例字段變量,線程不能共享本地變量和參數。原因是:本地變量和參數在一個線程方法中分配??叫堆棧。結果,每一個線程都收到它自己對那些變量的拷貝。相反,線程能夠共享類字段和實例字段因爲那些變量在一個線程方法(叫堆棧)中沒有被分配。取而代之,它們作爲類(類字段)或對象(實例字段)的一部份在共享內存堆中被分配。
這篇文章將教你如何使用同步連載線程訪問要害代碼部份。我用一個說明爲什麽一些多線程程序必須使用同步的例子作爲開始。我接下來就監視器和鎖探討Java的同步機制和synchronized 要害字。我通過研究由這樣的錯用産生的兩個問題判定經常因爲不正確的使用同步機制而否認了它的好處。
閱讀關于線程程序的整個系列:
? 第I部份:介紹線程、線程類及Runnable
? 第II部份:使用同步連載線程訪問要害代碼部份
對于同步的需要
爲什麽我們需要同步呢?一種回答,考慮這個例子:你寫一個使用一對線程模擬取款/存款金融事務的Java程序。在那個程序中,一個線程處理存款,同時其它線程正處理取款。每一個線程操作一對共享變量、類及實例字段變量,這些用來標識金融事務的姓名和賬號。對于一個正確的金融事務,每一個線程必須在其它線程開始給name和amount賦值前(並且同時打印那些值)給name和amount變量賦值(並打印那些值,模擬存貯事務)。其源代碼如下:
列表1. NeedForSynchronizationDemo.java
// NeedForSynchronizationDemo.java
class NeedForSynchronizationDemo
{
public static void main (String [] args)
{
FinTrans ft = new FinTrans ();
TransThread tt1 = new TransThread (ft, "Deposit Thread");
TransThread tt2 = new TransThread (ft, "Withdrawal Thread");
tt1.start ();
tt2.start ();
}
}
class FinTrans
{
public static String transName;
public static double amount;
}
class TransThread extends Thread
{
private FinTrans ft;
TransThread (FinTrans ft, String name)
{
super (name); //保存線程名稱
this.ft = ft; //保存對金融事務對象的引用
}
public void run ()
{
for (int i = 0; i
{
if (getName ().equals ("Deposit Thread"))
{
//存款線程要害代碼部份的開始
ft.transName = "Deposit";
try
{
Thread.sleep ((int) (Math.random () * 1000));
}
catch (InterruptedException e)
{
}
ft.amount = 2000.0;
System.out.println (ft.transName + " " + ft.amount);
//存款線程要害代碼部份的結束
}
else
{
//取款線程要害代碼部份的開始
ft.transName = "Withdrawal";
try
{
Thread.sleep ((int) (Math.random () * 1000));
}
catch (InterruptedException e)
{
}
ft.amount = 250.0;
System.out.println (ft.transName + " " + ft.amount);
//取款線程要害代碼部份的結束
}
}
}
}
NeedForSynchronizationDemo的源代碼有兩個要害代碼部份:一個可理解爲存款線程,另一個可理解爲取款線程。在存款線程要害代碼部份中,線程分配Deposit String對象的引用給共享變量transName及分配2000.0 給共享變量amount。同樣,在取款要害代碼部份,線程分配Withdrawal String對象的引用給transName及分配250.0給amount。在每個線程的分配之後打印那些變量的內容。當你運行NeedForSynchronizationDemo時,你可能期望輸出類似于Withdrawal 250.0 和Deposit 2000.0兩行組成的列表。