首頁 > Java > java教程 > 主體

Java 執行緒全和共享資源

黄舟
發布: 2017-02-28 10:37:29
原創
1582 人瀏覽過

程式碼被多執行緒同時的呼叫是安全的稱之為線程安全。如果一段程式碼是線程安全的,那麼它就不包含競態條件。競態條件只是發生在多執行緒更新共享資源的時候。因此知道Java執行緒什麼時間執行共享的資源是重要的。

局部變數

局部變數儲存在每一個執行緒自己的堆疊中。那意味著局部變數在執行緒之間不會共享。那也意味著所有的局部原始變數是線程安全的。這裡有一個例子:


public void someMethod(){

  long threadSafeInt = 0;

  threadSafeInt++;
}
登入後複製


#局部物件參考


對於物件的局部參考是有點不同的。這個引用本身是不會共享的。然而,這個物件的參考是不能儲存在每一個執行緒的棧中。所有的物件都儲存在共享的堆中。

如果局部建立的物件沒有逸出它所建立的方法,它是執行緒安全的。事實上你也可以把他傳遞給其他的方法,並且只要傳遞的這個物件的它的方法對於其他的線程是不可用的。

這裡有一個例子:


public void someMethod(){

  LocalObject localObject = new LocalObject();

  localObject.callMethod();
  method2(localObject);
}

public void method2(LocalObject localObject){
  localObject.setValue("value");
}
登入後複製


#在這個範例中的LocalObject實例不能從這個方法中傳回,也不能傳遞給其他的物件。那個從someMethod方法的外部是可以存取的。每個執行someMethod方法的執行緒將會建立它自己的LocalObject實例,並且把它指派給localObject參考。因此這種使用是線程安全的。

事實上,這整個someMethod方法是線程安全的。甚至如果這個localObject實例被當作參數傳遞給相同類別的其他方法,或是其他的類,它是執行緒安全的使用。

當然,唯一的異常,如果這些方法中的一個使用LocalObject作為參數調用,在某種程度上儲存LocalObject實例,允許來自其他執行緒的存取。

物件成員變數

物件成員變數(欄位)連同物件一起儲存在堆疊上。因此,如果兩個執行緒呼叫相同物件實例的一個方法,以及這個方法更新物件成員變量,整個方法就不是執行緒安全的。這裡有一個例子:


public class NotThreadSafe{
    StringBuilder builder = new StringBuilder();

    public add(String text){
        this.builder.append(text);
    }
}
登入後複製


如果兩個執行緒在相同的NotThreadSafe實例上同時的呼叫add方法,它就會導致競態條件。例如:



NotThreadSafe sharedInstance = new NotThreadSafe();

new Thread(new MyRunnable(sharedInstance)).start();
new Thread(new MyRunnable(sharedInstance)).start();

public class MyRunnable implements Runnable{
  NotThreadSafe instance = null;

  public MyRunnable(NotThreadSafe instance){
    this.instance = instance;
  }

  public void run(){
    this.instance.add("some text");
  }
}
登入後複製


#注意這兩個MyRunnable實例是怎麼樣分享相同的NotThreadSafe實例的。因此,當他們呼叫add方法的時候會導致競態條件。


然而,如果兩個執行緒在不同的實例上同時的呼叫add方法,它就不會導致競態條件。這裡是來自於之前的例子,稍微有些修改:


new Thread(new MyRunnable(new NotThreadSafe())).start();
new Thread(new MyRunnable(new NotThreadSafe())).start();
登入後複製

現在每一個執行緒都有一個他們自己的NotThreadSafe實例了,以至於他們呼叫這個add方法不會互相干擾。這個代碼不帶有競態條件。以至於,甚至一個物件不是線程安全的,它仍然可以用這種方式使用不會導致競態條件。

執行緒控制溢出規則

當嘗試決定是否你的程式碼對某個資源存取是執行緒安全的時候,你可以使用下列規則:


If a resource is created, used and disposed within
the control of the same thread,
and never escapes the control of this thread,
the use of that resource is thread safe.
登入後複製


資源可以是任何共享的資源,像一個對象,數組,文件,資料庫連接,套接字等等。在Java中,你不能總是明確的銷毀對象,以至於「銷毀的」意味著物件遺失或空的引用。


甚至如果一個物件的使用是線程安全的,如果那個物件指向了一個共享的資源像一個檔案或資料庫,你的應用程式作為一個整體可能就不是線程安全的了。例如,如果線程1和線程2各自創建自己的資料庫連接,連接1和連接2,每一個他們自己的連接使用是線程安全的。但是這個連接指向的資料庫的使用可能不是線程安全的。例如,如果兩個執行緒執行像這樣的程式碼:


check if record X exists
if not, insert record X
登入後複製


#如果兩個執行緒同時執行這個,以及他們正在檢查的這個記錄X放生在相同的記錄上,這裡就有一個風險,他們都會最終已插入而結束。如下所示:

Thread 1 checks if record X exists. Result = no
Thread 2 checks if record X exists. Result = no
Thread 1 inserts record X
Thread 2 inserts record X
登入後複製


這個也會發生在操作在檔案或其他的共享資源的執行緒上。因此去區分被一個執行緒控制的物件是否是一個資源,或者只是這個資源(像資料庫連結)的引用是重要的。

 以上就是Java 執行緒全和共享資源的內容,更多相關內容請關注PHP中文網(www.php.cn)!


#
相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板