首頁  >  文章  >  Java  >  Java 執行緒全和共享資源

Java 執行緒全和共享資源

黄舟
黄舟原創
2017-02-28 10:37:291574瀏覽

程式碼被多執行緒同時的呼叫是安全的稱之為線程安全。如果一段程式碼是線程安全的,那麼它就不包含競態條件。競態條件只是發生在多執行緒更新共享資源的時候。因此知道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)!


#
陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn