java 访问线程_java线程简介(共享对数据的访问)
1、 共享變量
要使多個線程在一個程序中有用,它們必須有某種方法可以互相通信或共享它們的結果。
讓線程共享其結果的最簡單方法是使用共享變量。它們還應該使用同步來確保值從一個線程正確傳播到另一個線程,以及防止當一個線程正在更新一些相關數據項時,另一個線程看到不一致的中間結果。
線程基礎中計算素數的示例使用了一個共享布爾變量,用于表示指定的時間段已經過去了。這說明了在線程間共享數據最簡單的形式是:輪詢共享變量以查看另一個線程是否已經完成執行某項任務。
2、存在于同一個內存空間中的所有線程
正如前面討論過的,線程與進程有許多共同點,不同的是線程與同一進程中的其它線程共享相同的進程上下文,包括內存。這非常便利,但也有重大責任。只要訪問共享變量(靜態或實例字段),線程就可以方便地互相交換數據,但線程還必須確保它們以受控的方式訪問共享變量,以免它們互相干擾對方的更改。
任何線程可以訪問所有其作用域內的變量,就象主線程可以訪問該變量一樣。素數示例使用了一個公用實例字段,叫做 finished,用于表示已經過了指定的時間。當計時器過期時,一個線程會寫這個字段;另一個線程會定期讀取這個字段,以檢查它是否應該停止。注:這個字段被聲明成 volatile,這對于這個程序的正確運行非常重要。在本章的后面,我們將看到原因。
3、受控訪問的同步
為了確??梢栽诰€程之間以受控方式共享數據,Java 語言提供了兩個關鍵字:synchronized 和 volatile。
Synchronized 有兩個重要含義:它確保了一次只有一個線程可以執行代碼的受保護部分(互斥,mutual exclusion 或者說 mutex),而且它確保了一個線程更改的數據對于其它線程是可見的(更改的可見性)。
如果沒有同步,數據很容易就處于不一致狀態。例如,如果一個線程正在更新兩個相關值(比如,粒子的位置和速率),而另一個線程正在讀取這兩個值,有可能在第一個線程只寫了一個值,還沒有寫另一個值的時候,調度第二個線程運行,這樣它就會看到一個舊值和一個新值。同步讓我們可以定義必須原子地運行的代碼塊,這樣對于其他線程而言,它們要么都執行,要么都不執行。
同步的原子執行或互斥方面類似于其它操作環境中的臨界段的概念。
4、確保共享數據更改的可見性
同步可以讓我們確保線程看到一致的內存視圖。
處理器可以使用高速緩存加速對內存的訪問(或者編譯器可以將值存儲到寄存器中以便進行更快的訪問)。在一些多處理器體系結構上,如果在一個處理器的高速緩存中修改了內存位置,沒有必要讓其它處理器看到這一修改,直到刷新了寫入器的高速緩存并且使讀取器的高速緩存無效。
這表示在這樣的系統上,對于同一變量,在兩個不同處理器上執行的兩個線程可能會看到兩個不同的值!這聽起來很嚇人,但它卻很常見。它只是表示在訪問其它線程使用或修改的數據時,必須遵循某些規則。
Volatile 比同步更簡單,只適合于控制對基本變量(整數、布爾變量等)的單個實例的訪問。當一個變量被聲明成 volatile,任何對該變量的寫操作都會繞過高速緩存,直接寫入主內存,而任何對該變量的讀取也都繞過高速緩存,直接取自主內存。這表示所有線程在任何時候看到的 volatile 變量值都相同。
如果沒有正確的同步,線程可能會看到舊的變量值,或者引起其它形式的數據損壞。
5、用鎖保護的原子代碼塊
Volatile 對于確保每個線程看到最新的變量值非常有用,但有時我們需要保護比較大的代碼片段,如涉及更新多個變量的片段。
同步使用監控器(monitor)或鎖的概念,以協調對特定代碼塊的訪問。
每個 Java 對象都有一個相關的鎖。同一時間只能有一個線程持有 Java 鎖。當線程進入 synchronized 代碼塊時,線程會阻塞并等待,直到鎖可用,當它可用時,就會獲得這個鎖,然后執行代碼塊。當控制退出受保護的代碼塊時,即到達了代碼塊末尾或者拋出了沒有在 synchronized 塊中捕獲的異常時,它就會釋放該鎖。
這樣,每次只有一個線程可以執行受給定監控器保護的代碼塊。從其它線程的角度看,該代碼塊可以看作是原子的,它要么全部執行,要么根本不執行。
6、簡單的同步示例
使用 synchronized 塊可以讓您將一組相關更新作為一個集合來執行,而不必擔心其它線程中斷或看到計算的中間結果。以下示例代碼將打印“1 0”或“0 1”。如果沒有同步,它還會打印“1 1”(或“0 0”,隨便您信不信)。
public class SyncExample {
private static lockObject = new Object();
private static class Thread1 extends Thread {
public void run() {
synchronized (lockObject) {
x = y = 0;
System.out.println(x);
}
}
}
private static class Thread2 extends Thread {
public void run() {
synchronized (lockObject) {
x = y = 1;
System.out.println(y);
}
}
}
public static void main(String[] args) {
new Thread1().run();
new Thread2().run();
}
}
在這兩個線程中都必須使用同步,以便使這個程序正確工作。
7、Java 鎖定
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();
}
}
8、同步的方法
創建 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 方法。
9、同步的塊
synchronized 塊的語法比 synchronized 方法稍微復雜一點,因為還需要顯式地指定鎖要保護哪個塊。Point 的以下版本等價于前一頁中顯示的版本:
public class Point {
public void setXY(int x, int y) {
synchronized (this) {
this.x = x;
this.y = y;
}
}
}
使用 this 引用作為鎖很常見,但這并不是必需的。這表示該代碼塊將與這個類中的 synchronized 方法使用同一個鎖。
由于同步防止了多個線程同時執行一個代碼塊,因此性能上就有問題,即使是在單處理器系統上。最好在盡可能最小的需要保護的代碼塊上使用同步。
訪問局部(基于堆棧的)變量從來不需要受到保護,因為它們只能被自己所屬的線程訪問。
10、大多數類并沒有同步
因為同步會帶來小小的性能損失,大多數通用類,如 java.util 中的 Collection 類,不在內部使用同步。這表示在沒有附加同步的情況下,不能在多個線程中使用諸如 HashMap 這樣的類。
總結
以上是生活随笔為你收集整理的java 访问线程_java线程简介(共享对数据的访问)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 360积分有什么用?
- 下一篇: Photoshop各类抠图技巧介绍