看了文章一,應該對線程有個初步的認識了,我再簡單的介紹一下線程使用中的一些內容.
線程通過幾種機制進入 Java 程序。除了用 Thread 構造器中顯式創建線程之外,還可以用許多其它機制創建線程:
· AWT 和 Swing
· RMI
· java.util.TimerTask 工具
· servlet 和 JSP 技術
我只是簡單的提一下后兩種.
TimerTask.
JDK 1.3 中,TimerTask 工具被引入到 Java 語言。這個便利的工具讓您可以稍后在某個時間執行任務(例如,即從現在起十秒后運行一次任務),或者定期執行任務(即,每隔十秒運行任務)。
實現 Timer 類非常簡單:它創建一個計時器線程,并且構建一個按執行時間排序的等待事件隊列。
TimerTask 線程被標記成守護程序線程,這樣它就不會阻止程序退出。
servlet 容器創建多個線程,在這些線程中執行 servlet 請求。作為 servlet 編寫者,您不知道(也不應該知道)您的請求會在什么線程中執行;如果同時有多個對相同 URL 的請求入站,那么同一個 servlet 可能會同時在多個線程中是活動的。
當編寫 servlet 或 JavaServer Pages (JSP) 文件時,必須始終假設可以在多個線程中并發地執行同一個 servlet 或 JSP 文件。必須適當同步 servlet 或 JSP 文件訪問的任何共享數據;這包括servlet 對象本身的字段。
下面說一下共享數據的訪問,這也是線程的一個很重要的部分.
要使多個線程在一個程序中有用,它們必須有某種方法可以互相通信或共享它們的結果。
讓線程共享其結果的最簡單方法是使用共享變量。它們還應該使用同步來確保值從一個線程正確傳播到另一個線程,以及防止當一個線程正在更新一些相關數據項時,另一個線程看到不一致的中間結果。有關兩個關鍵字:synchronized 和volatile以及java鎖,我已經在上一篇文章里面提到了,這里我再深入的介紹一下.
先復述一下java鎖,
每個 Java 對象都有一個相關的鎖。同一時間只能有一個線程持有 Java 鎖。當線程進入
synchronized 代碼塊時,線程會阻塞并等待,直到鎖可用,當它可用時,就會獲得這個鎖,然后執行代碼塊。當控制退出受保護的代碼塊時,即到達了代碼塊末尾或者拋出了沒有在 synchronized 塊中捕獲的異常時,它就會釋放該鎖。
Java 鎖定合并了一種互斥形式。每次只有一個線程可以持有鎖。鎖用于保護代碼塊或整個方法,必須記住是鎖的身份保護了代碼塊,而不是代碼塊本身,這一點很重要。一個鎖可以保護許多代碼塊或方法。
反之,僅僅因為代碼塊由鎖保護并不表示兩個線程不能同時執行該代碼塊。它只表示如果兩個線程正在等待相同的鎖,則它們不能同時執行該代碼。
下面這個實例代碼展示就是后面這種情況. ,兩個線程可以同時不受限制地執行 setLastAccess() 中的 synchronized 塊,因為每個線程有一個不同的 thingie 值。因此,synchronized 代碼塊受到兩個正在執行的線程中不同鎖的保護。
public class SyncExample {
public static class Thingie {
private Date lastAccess;
public synchronized void setLastAccess(Date date) {
this.lastAccess = date;
}
}
public static class MyThread extends Thread {
private Thingie thingie;
public MyThread(Thingie thingie) {
this.thingie = thingie;
}
public void run() {
thingie.setLastAccess(new Date());
}
}
public static void main() {
Thingie thingie1 = new Thingie(),
thingie2 = new Thingie();
new MyThread(thingie1).start();
new MyThread(thingie2).start();
}
}
同步方法
創建 synchronized 塊的最簡單方法是將方法聲明成 synchronized。這表示在進入方法主體之前,
調用者必須獲得鎖:
public class Point {
public synchronized void setXY(int x, int y) {
this.x = x;
this.y = y;
}
}
對于普通的 synchronized 方法,這個鎖是一個對象,將針對它調用方法。對于靜態 synchronized方法,這個鎖是與 Class 對象相關的監控器,在該對象中聲明了方法。
僅僅因為 setXY() 被聲明成 synchronized 并不表示兩個不同的線程不能同時執行 setXY(),只要它們調用不同的 Point 實例的 setXY() 就可同時執行。對于一個 Point 實例,一次只能有一個線程執行 setXY(),或 Point 的任何其它 synchronized 方法。
如以下代碼樣本所示,SimpleCache.java 使用 HashMap 為對象裝入器提供了一個簡單的高速緩存。
load() 方法知道怎樣按對象的鍵裝入對象。在一次裝入對象之后,該對象就被存儲到高速緩存中,這樣以后的訪問就會從高速緩存中檢索它,而不是每次都全部地裝入它。對共享高速緩存的每個訪問都受到 synchronized 塊保護。由于它被正確同步,所以多個線程可以同時調用 getObject 和clearCache 方法,而沒有數據損壞的風險。
public class SimpleCache {
private final Map cache = new HashMap();
public Object load(String objectName) {
// load the object somehow
}
public void clearCache() {
synchronized (cache) {
cache.clear();
}
}
public Object getObject(String objectName) {
synchronized (cache) {
Object o = cache.get(objectName);
if (o == null) {
o = load(objectName);
cache.put(objectName, o);
}
}
return o;
}
}
由于線程執行的計時是不確定的,我們需要小心,以控制線程對共享數據的訪問。否則,多個并發線程會互相干擾對方的更改,從而損壞數據,或者其它線程也許不能及時看到對共享數據的更改。通過使用同步來保護對共享變量的訪問,我們可以確保線程以可預料的方式與程序變量進行交互。每個 Java 對象都可以充當鎖,synchronized 塊可以確保一次只有一個線程執行由給定鎖保護的synchronized 代碼。
那么什么時候需要使用同步呢?
要跨線程維護正確的可見性,只要在幾個線程之間共享非 final 變量,就必須使用 synchronized(或 volatile)以確保一個線程可以看見另一個線程做的更改。
可見性同步的基本規則是在以下情況中必須同步:
· 讀取上一次可能是由另一個線程寫入的變量
· 寫入下一次可能由另一個線程讀取的變量
許多 Java 類,包括 String、Integer 和 BigDecimal,都是不可改變的:一旦構造之后,它們的狀態就永遠不會更改。如果某個類的所有字段都被聲明成 final,那么這個類就是不可改變的。(實際上,許多不可改變的類都有非 final 字段,用于高速緩存以前計算的方法結果,如String.hashCode(),但調用者看不到這些字段。)不可改變的類使并發編程變得非常簡單。因為不能更改它們的字段,所以就不需要擔心把狀態的更
改從一個線程傳遞到另一個線程。在正確構造了對象之后,可以把它看作是常量。
同樣,final 字段對于線程也更友好。因為 final 字段在初始化之后,它們的值就不能更改,所以當在線程之間共享 final 字段時,不需要擔心同步訪問。
什么時候不需要同步呢?
在某些情況中,您不必用同步來將數據從一個線程傳遞到另一個,因為 JVM 已經隱含地為您執行同步。這些情況包括:
· 由靜態初始化器(在靜態字段上或 static{} 塊中的初始化器)初始化數據時
· 訪問 final 字段時
· 在創建線程之前創建對象時
· 線程可以看見它將要處理的對象時
再介紹一個概念,死鎖.
只要您擁有多個進程,而且它們要爭用對多個鎖的獨占訪問,那么就有可能發生死鎖。如果有一組進程或線程,其中每個都在等待一個只有其它進程或線程才可以執行的操作,那么就稱它們被死鎖了。
最常見的死鎖形式是當線程 1 持有對象 A 上的鎖,而且正在等待與 B 上的鎖,而線程 2 持有對象 B 上的鎖,卻正在等待對象 A 上的鎖。這兩個線程永遠都不會獲得第二個鎖,或者釋放第一個鎖。它們只會永遠等待下去。要避免死鎖,應該確保在獲取多個鎖時,在所有的線程中都以相同的順序獲取鎖。
同步準則
當編寫 synchronized 塊時,有幾個簡單的準則可以遵循,這些準則在避免死鎖和性能危險的風險方面大有幫助:
代碼塊要簡短Synchronized 塊應該簡短 — 在保證相關數據操作的完整性的同時,
盡量簡短。把不隨線程變化的預處理和后處理移出 synchronized 塊。
不要阻塞。不要在 synchronized 塊或方法中調用可能引起阻塞的方法,如
則呢arInputStream.read()。
在持有鎖的時候,不要對其它對象調用方法。這聽起來可能有些極端,但它消除了最常見的死鎖源頭。
有關線程的編程還有很多內容,我只是介紹一個初級的入門知識,我也是才學習線程了,文章主要內容是根據網上流傳的資料改變整理.
核心: 勇敢進取年輕的心