tutorials sitehtml
單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.多線程
使用併發的好處:併發
* 更好的資源利用率 * 更精簡的程序設計(一個線程負責讀,一個線程負責寫,一個線程只作一個功能,這樣程序設計精簡多了) * 更多響應的程序 (服務器監聽線程,處理線程的例子)
使用併發的代價: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
start 和 run的區別說明ui
* start() 的做用是 啓動一個新線程(操做系統級別,有一個native方法start0() 啓動新線程),新線程會執行相應的run方法。 * run() 和普通的成員方法同樣,能夠被重複調用。 單獨調用run() 會在當前線程中執行run() 並不會啓動新線程
建立一個線程Thread thread = new Thread(); thread.start() 便可
可是這個線程沒有執行任何代碼段。
有兩種方式能夠指定哪段代碼 一個線程會執行。
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();
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();
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 acritical section
. In the previous example the method add() is a critical section, leading to race conditions. Race conditions can be avoided by properthread synchronization
in critical sections.
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 不共享的資源有:
thread safe
.public void someMethod(){ long threadSafeInt = 0; threadSafeInt++; }
Java 共享的資源有:
好比以下的例子,由於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 來判斷是否代碼訪問的資源是線程安全的。
若是一個資源在一個線程的控制下被建立、使用和銷燬而且永遠不會逃脫線程的控制,則該資源的使用是線程安全的。