併發學習筆記(1)

tutorials sitehtml

併發Concurrency發展的歷史

單CPU,一次只能運行一個程序 -- 多任務,一次同時運行多個任務 (問題是每一個任務不能永遠佔有資源或者CPU,再也不使用資源或者CPU的程序須要釋放掉本身的資源) -- 多線程,每一個任務都有多線程進行處理,每一個執行着的線程均可以當作是一個CPU。(問題是 每一個線程都執行相同的任務,所以同時讀和寫同一段內存,這會致使單線程不能出現的問題)
     舉個例子: 若是一個線程讀了一塊內存 同時發生了另外一個線程寫到了這塊內存。那麼第一個線程讀到的是什麼? old value or the value just wriiten or a value mid between the two. 若是更多的線程同時寫到了這個內存,第一個線程讀到什麼。
     Therefore it is important as a developer to know how to take the right precautions - meaning learning to control how threads access shared resources like memory, files, databases etc. That is one of the topics this Java concurrency tutorial addresses.

多進程和多線程的區別?
本質的區別在於每一個進程擁有本身的一整套變量,而線程則共享數據。共享數據使線程之間的通訊比進程之間的通訊更有效。java

此外在有些操做系統中,與進程相比較,線程更加輕量級,建立,撤銷一個線程比啓動線程開銷要小不少。web

棧的組成

JVM 的內存模型 能夠分爲調用棧,堆,方法區,寄存器,本地方法棧;其中主要組成是前二。
圖片描述
同一進程中的多條線程將共享該進程中的所有系統資源,如虛擬地址空間,文件描述符和信號處理等等。但同一進程中的多個線程有各自的調用棧(call stack),本身的寄存器環境(register context),本身的線程本地存儲(thread-local storage)安全

每個在JVM中運行的線程都有本身的調用棧call stack, 調用棧存儲着該線程調用了哪些方法以及這些方法的局部變量。每一個棧只能查看本身的局部變量,沒法查看其它棧的局部變量。服務器

Objects on the heap can be accessed by all threads that have a reference to the object. When a thread has access to an object, it can also get access to that object's member variables. If two threads call a method on the same object at the same time, they will both have access to the object's member variables, but each thread will have its own copy of the local variables.多線程

圖片描述
圖片描述

 advantage and disadvantage

使用併發的好處:併發

* 更好的資源利用率
* 更精簡的程序設計(一個線程負責讀,一個線程負責寫,一個線程只作一個功能,這樣程序設計精簡多了)
* 更多響應的程序 (服務器監聽線程,處理線程的例子)

使用併發的代價:app

* 設計更復雜

Code executed by multiple threads accessing shared data need special attention. Thread interaction is far from always simple. Errors arising from incorrect thread synchronization can be very hard to detect, reproduce and fix.

* 上下文切換

When a CPU switches from executing one thread to executing another, the CPU needs to save the local data, program pointer etc. of the current thread, and load the local data, program pointer etc. of the next thread to execute. This switch is called a "context switch".

* 消耗資源

CPU須要額外的空間時間去維護一個線程。

併發模型ide

Creating and Starting java threads

start 和 run的區別說明ui

* start() 的做用是 啓動一個新線程(操做系統級別,有一個native方法start0() 啓動新線程),新線程會執行相應的run方法。
* run() 和普通的成員方法同樣,能夠被重複調用。 單獨調用run() 會在當前線程中執行run() 並不會啓動新線程

建立一個線程Thread thread = new Thread(); thread.start() 便可
可是這個線程沒有執行任何代碼段。

有兩種方式能夠指定哪段代碼 一個線程會執行。

  • 繼承Thread - Thread Subclass
javapublic class MyThread extends Thread {

    public void run(){
       System.out.println("MyThread running");
    }
  }

To create and start the above thread you can do like this:

javaMyThread myThread = new MyThread();
  myTread.start();
  • 覆蓋run方法 - Runnable Interface Implemention
    第二種方式指定線程應該運行那端代碼 是建立一個類執行java.lang.Runnable接口
public class MyRunnable implements Runnable {

    public void run(){
       System.out.println("MyRunnable running");
    }
  }

To have the run() method executed by a thread, pass an instance of MyRunnable to a Thread in its constructor. Here is how that is done:

Thread thread = new Thread(new MyRunnable());
thread.start();

Race Conditions and Critical Sections

The problems arise when multiple threads access the same resources.For instance the same memory (variables, arrays, or objects), systems (databases, web services etc.) or files. In fact, problems only arise if one or more of the threads write to these resources. It is safe to let multiple threads read the same resources, as long as the resources do not change.

The situation where two threads compete for the same resource, where the sequence in which the resource is accessed is significant, is called race conditions. A code section that leads to race conditions is called a critical section. In the previous example the method add() is a critical section, leading to race conditions. Race conditions can be avoided by proper thread synchronization in critical sections.

Thread Safety and Shared Resources

Code that is safe to call by multiple threads simultanously is called thread safe. If a piece of code is thread safe, then it contains no race conditions. Race condition only occur when multiple threads update shared resources. Therefore it is important to know what resources Java threads share when executing.

Java 不共享的資源有:

  • 局部變量 Local varables
    局部變量(方法內部變量)存儲在每一個線程本身的棧裏面,這意味着局部變量不會被多個線程共享。這也意味着全部局部變量都是線程安全的thread safe.
    好比:
public void someMethod(){
  long threadSafeInt = 0;
  threadSafeInt++;
}

Java 共享的資源有:

  • 局部對象引用 Local Object References
    引用自己是線程安全的,由於他的局部變量。可是引用所☞指的對象object並不是存儲在線程的局部棧的,而是存儲在共享堆裏 shared heap。 每一個線程在各自的方法內建立的局部對象,只要不做爲返回值返回,其餘線程訪問不到,不產生競爭就不會有安全問題

好比以下的例子,由於localObject沒有做爲返回值返回,其餘的線程獲取不到這個對象的

javapublic void someMethod(){
  LocalObject localObject = new LocalObject();
  localObject.callMethod();
  method2(localObject);
}

public void method2(LocalObject localObject){
  localObject.setValue("value");
}
  • 對象成員變量
    對象成員與對象一塊兒存儲在堆裏面,對象成員是屬於類的,局部對象引用是屬於方法的,不一樣的線程訪問或者修改一個類的成員時就會存在競爭.

好比這個例子中,方法add就是線程不安全的(由於build是對象成員變量,多線程都對它同時操做)。

public class NotThreadSafe{
  StringBuilder builder = new StringBuilder();
  public add(String text){
    this.builder.append(text);
  }
}

  若是兩個線程同時調用同一個 NotThreadSafe 實例的 add() 方法就會引發race condition。好比:

NotThreadSafe sharedInstance = new NotThreadSafe();
new Thread(new MyRunnable(sharedInstance)).start(); // 線程1
new Thread(new MyRunnable(sharedInstance)).start(); // 線程2
public class MyRunnable implements Runnable{
  NotThreadSafe instance = null;
  public MyRunnable(NotThreadSafe instance){
    this.instance = instance;
  }
  public void run(){
    this.instance.add("some text");
  }
}

然而若是兩個線程在不一樣的實例上面同時調用 add() 方法並不會引發靜態條件。下面是稍微修改以後的例子:

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

  如今這兩個線程都有本身的 NotThreadSafe 實例,因此它們對 add 方法的調用並不會妨礙對方,這段代碼沒有競態條件。因此即便一個對象不是線程安全的,仍能夠找到一個方式來消除競態條件。 
可使用線程逃逸準則 Thread Control Escape Rule 來判斷是否代碼訪問的資源是線程安全的。

若是一個資源在一個線程的控制下被建立、使用和銷燬而且永遠不會逃脫線程的控制,則該資源的使用是線程安全的。

相關文章
相關標籤/搜索