Java的多線程學習
這裏主要是對我之前學習java多線程知識的一個總結,之前的內容都記在筆記本上。
一.線程的概念
線程是輕量化的進程,而進程就是運行中的程序。一個程序至少包含一個進程,而一個進程至少包含一個線程。線程是進程的執行單元。
進程(Process):是一個動態概念,擁有完整的地址空間,不依賴於線程而獨立存在。
線程(Thread):屬於進程的一部分,不能單獨運行,沒有自己的地址空間,與進程內的其他線程一起共享分配給該進程的所有資源。
線程可以有效地提高系統的執行效率,進行多任務的處理,注意是併發而不是並行處理。線程的創建和結束所需要的系統開銷要比進程的創建和結束要小得多。
二.Java創建線程
Thread類代表線程,所有線程對象都必須是Thread類或其子類的實例。它在lang包下。
創建線程的三種方法:
1.繼承Thread類:重寫run()方法
2.實現Runnable接口:實現run()方法(用的最多)
3.用Callable和Future實現:實現call()方法
實際中實現Runnable這種方法用的多,這種情況可以多個線程共享一個目標對象。如下例子,兩個新建的線程共享s的實例屬性。
例:
class Sample implements Runnable
{
private int i;
public void run()
{
for(;i<100;i++){ System.out.println(Thread.currentThread().getName()+" "+i);}
}
public static void main(String[] agrs)
{
Sample s=new Sample();
new Thread(s,"線程1").start(); //兩個線程共享一個Sample對象
new Thread(s,"線程2").start();
}
}
三.線程的狀態
新建,就緒,阻塞,運行,死亡
調用Thread的start()方法後,線程進入就緒狀態。對於死亡狀態,也就是結束狀態,run()和call()執行完成,或者拋出一個未捕獲的異常,或者直接調用Thread的stop()方法,這三種情況都會讓線程進入死亡狀態。
Thread.sleep(X)方法讓線程進入阻塞狀態,直到時間完成,就會進入就緒狀態,等待再度被調用。該方法需要處理異常。
Thread.yield()方法讓線程進入到就緒狀態。
Java線程有10個優先級。
四.線程的同步處理
這段主要是關於synchronized的內容,其他的同步處理在後面的文章中給出。多線程編程伴隨着同步處理,多個線程如果同時處理一個數據,這時該怎麼辦,這就是線程同步要處理的問題。線程的控制Java給出了很多類,特別是Java5後面給出的java.util.concurret下的類。.
同步處理的關鍵字:synchronized和volatile
synchronized用於修飾代碼塊和方法,被它修飾過的就稱爲同步代碼塊或同步方法。 它是一種“加鎖---->修改----->釋放鎖”。
同步代碼塊中 synchronized(obj){.........} 鎖住的就是obj對象,obj可以是this,代表該對象。運行代碼塊中內容前,會去對obj進行加鎖,如果obj被其他線程鎖定了,則它只能等待obj被釋放。在加鎖期間,其他線程無法獲取該資源。Java的io包中很多類都新建了一個object變量當做鎖,進行同步處理。
1.synchronized鎖住的是對象,不是代碼,各種線程之前還是會被調用切換。
2.使用synchronized,必須同時進行同步處理,否有一個線程有synchronized,一個線程沒有synchronized,這是沒有效果的。沒加synchronized的線程根本不會進行加鎖。
3.要儘量減少鎖的粒度,這樣是程序能更大程度的併發。能用代碼塊的不要用同步方法。
4.synchronized同步機制不僅內存同步,而且具有互斥性。volatile僅僅是內存同步。
5.synchronized的對象應選擇不會改變的對象,如final修飾的,或者專門新建的Object,否則會出現意想不到的錯誤。
volatile關鍵字用於修飾變量,用於保證所有線程對該變量使用時的一致性。volatile聲明變量值的一致性,而static申明類變量的唯一性。volatile使用時可能會是不安全的,如在i++操作下,用volatile進行併發處理可能出錯。因爲i++(自增運算)在Java下並非是原子操作。
5.死鎖
使用同步處理會導致死鎖的發生,當兩個線程相互等待對方釋放同步監視器時就會發生死鎖。一旦出現死鎖,程序不會有異常,也不會有提示,程序處於阻塞狀態。
這裏給一個死鎖的例子:
public class DeadLock implements Runnable
{
private int a;
private int b;
public DeadLock(int a ,int b)
{
this.a=a;
this.b=b;
}
public void run()
{
synchronized(Integer.valueOf(a))
{
try
{
Thread.sleep(200); //sleep()方法必須進行異常處理,且run()方法不能拋出異常,所以只能用try..catch
synchronized(Integer.valueOf(b))
{
System.out.println("進入內部");
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
public static void main(String[] args)
{
DeadLock d1=new DeadLock(1,2);
DeadLock d2=new DeadLock(2,1);
new Thread(d1).start(); //對1加鎖,在對2加鎖時,d2那個線程已經對2加了鎖,這樣兩個線程相互等待。
new Thread(d2).start();
System.out.println("主線程結束");
}
}
結果是程序阻塞,無法結束,看不到線程輸出"進入內部":
參考:《瘋狂Java講義》
《深入理解Java虛擬機》
http://blog.csdn.net/hsuxu/article/details/9472415