Thread safety and Shared Resource

一、Local Variables

Local variables are stored in each thread’s own stack. That means that local variables are never shared between threads:
That also means that all local primitive variables are thread safe

二、Local Object References

If an object created locally never escapes the method it was created in, it is thread safe. In fact you can also pass it on to other methods and objects as long as none of these methods or objects make the passed object available to other threads.
引用在棧中,是不會共享的;對象存放在共享堆中。如果一個對象不離開此方法,則是線程安全的。假設對象爲A,若以參數形式傳給其他方法或對象,只要這些方法或對象不把A給其他線程,則是線程安全的。

public void someMethod(){

  LocalObject localObject = new LocalObject();

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

public void method2(LocalObject localObject){
  localObject.setValue("value");
}

The LocalObject instance in this example is not returned from the method, nor is it passed to any other objects that are accessible from outside the someMethod(). Each thread executing the someMethod() will create its own LocalObject instance and assign it to the localObject reference. Therefore the use of theLocalObject here is thread safe.
LocalObject實例既沒有作爲返回值,也沒將它傳給的其他對象(這些對象在someMethod外是可訪問的)。每個執行someMethod方法的線程都會創建自己的LocalObject實例和引用。因此,LocalObject使用時線程安全的。

The only exception is of course, if one of the methods called with the LocalObject as parameter, stores the LocalObject instance in a way that allows access to it from other threads.
例外,以LocalObject 爲參數的方法存儲LocalObject 實例,這個實例能夠讓其他線程訪問。

三、Object Member Variables

Object member variables (fields) are stored on the heap along with the object. Therefore, if two threads call a method on the same object instance and this method updates object member variables, the method is not thread safe. Here is an example of a method that is not thread safe:
類成員變量被存放在堆中。因此,若兩個線程調用同一個實例的相同方法,則這個方法更新類成員變量,此時這個方法並不是線程安全的。

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

    public add(String text){
        this.builder.append(text);
    }
}

If two threads call the add() simultaneously on the same NotThreadSafe instance then it leads to race conditions. For instance:
若兩個線程同時調用相同的 NotThreadSafe實例 的add(),則會引發race conditions

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");
  }
}

However, if two threads call theadd() simultaneously on different instances then it does not lead to race condition. Here is the example from before, but slightly modified:

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

Now the two threads have each their own instance of NotThreadSafe.so their calls to the add doesn’t interfere with each other. The code does not have race condition anymore. So, even if an object is not thread safe it can still be used in a way that doesn’t lead to race condition.
因爲兩個線程有各自的NotThreadSafe實例,因此線程調用add()方法並不會相互干擾,所以不會有race condition。因此,儘管一個對象是非線程安全的,它也可以被使用,而不會導致race condition。【並不是非線程安全,就一定會引發race condition。race condition引發的條件是多線程對相同資源同時讀寫】

四、The Thread Control Escape Rule

When trying to determine if your code’s access of a certain resource is thread safe you can use the thread control escape rule:

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.

Resources can be any shared resource like an object, array, file, database connection, socket etc. In Java you do not always explicitly dispose objects, so “disposed” means losing or null’ing the reference to the object.

Even if the use of an object is thread safe, if that object points to a shared resource like a file or database, your application as a whole may not be thread safe. For instance, if thread 1 and thread 2 each create their own database connections, connection 1 and connection 2, the use of each connection itself is thread safe. But the use of the database the connections point to may not be thread safe.
即使對象的使用是線程安全的,如果對象指向共享資源,應用作爲一個整體也許不是線程安全的。例如,線程1和線程2各自創建自己的數據庫連接,連接1和連接2,各自的連接本身的使用是線程安全。但是連接指向的數據庫使用可能不是線程安全的。
For example, if both threads execute code like this:

check if record X exists
if not, insert record X

If two threads execute this simultaneously, and the record X they are checking for happens to be the same record, there is a risk that both of the threads end up inserting it. This is how:
如果兩個線程同時執行,它們校驗的記錄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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章