“你好,阿米戈!昨天我們討論了多線程的好處和便利。現在是時候看看缺點了。而且,不幸的是,它們並不小。”

以前,我們將程序視為一組相互調用方法的對象。現在一切都變得有點複雜了。一個程序更像是一組對象,有幾個“小機器人”(線程)在其中爬行並執行方法中包含的命令。

這個新的解釋並沒有取消第一個。它們仍然是對象,它們仍然調用彼此的方法。但是我們必須記住,有幾個線程,每個線程都有自己的工作或任務。

程序變得越來越複雜。不同的線程根據它們執行的任務改變不同對象的狀態。他們可以踩到對方的腳趾。

但最糟糕的事情發生在 Java 機器的深處。正如我已經說過的,線程表面上的同時性是通過處理器不斷地從一個線程切換到另一個線程這一事實實現的。它切換到一個線程,工作 10 毫秒,切換到下一個線程,工作 10 毫秒,依此類推。問題就在這裡:這些轉換可能發生在最不合時宜的時刻。考慮這個例子:

第一個線程的代碼 第二個線程的代碼
System.out.print ("Nick is");
System.out.print ("");
System.out.print ("15");
System.out.print ("");
System.out.print ("years old");
System.out.println ();
System.out.print ("Lena is");
System.out.print ("");
System.out.print ("21");
System.out.print ("");
System.out.print ("years old");
System.out.println ();
我們期望顯示的內容
尼克 15 歲
莉娜 21 歲
實際代碼執行 第一個線程的代碼 第二個線程的代碼
System.out.print ("Nick is");
System.out.print ("Lena is");
System.out.print (" ");
System.out.print (" ");
System.out.print ("15");
System.out.print ("21");
System.out.print (" ");
System.out.print (" ");
System.out.print ("years old");
System.out.println ();
System.out.print ("years old");
System.out.println ();
System.out.print ("Nick is");
//other thread is running
//other thread is running
System.out.print (" ");
System.out.print ("15");
//other thread is running
//other thread is running
System.out.print (" ");
System.out.print ("years old");
System.out.println ();
//other thread is running
//other thread is running
//other thread is running
System.out.print ("Lena is");
System.out.print (" ");
//other thread is running
//other thread is running
System.out.print ("21");
System.out.print (" ");
//other thread is running
//other thread is running
//other thread is running
System.out.print ("years old");
System.out.println ();
實際顯示的是什麼
Nick is  Lena   15 21 

這是另一個例子:

代碼 描述
class MyClass
{
private String name1 = "Ally";
private String name2 = "Lena";
public void swap()
{
String s = name1;
name1 = name2;
name2 = s;
}
}
交換方法和變量swap的值。name1name2

如果同時從兩個線程調用它會發生什麼?

實際代碼執行 第一個線程的代碼 第二個線程的代碼
String s1 = name1; //Ally
name1 = name2; //Lena
String s2 = name1; //Lena(!)
name1 = name2; //Lena
name2 = s1; //Ally
name2 = s2; //Lena
String s1 = name1;
name1 = name2;
//other thread is running
//other thread is running
name2 = s1;
//other thread is running
//other thread is running
//other thread is running
String s2 = name1;
name1 = name2;
//other thread is running
name2 = s2;
底線
兩個變量的值都是 «Lena»。
«Ally» 對像沒有成功。它丟失了。

“誰能想到這麼簡單的賦值操作竟然會出現這樣的錯誤?!”

“是的,這個問題有解決辦法。但我們稍後再談——我的喉嚨很乾。”