在說線程以前先說下進程,進程和線程都是一個時間段的描述,是CPU工做時間段的描述。java
進程,是併發執行的程序在執行過程當中分配和管理資源的基本單位,是一個動態概念,竟爭計算機系統資源的基本單位。每個進程都有一個本身的地址空間,即進程空間或(虛空間)。git
線程,在網絡或多用戶環境下,一個服務器一般須要接收大量且不肯定數量用戶的併發請求,爲每個請求都建立一個進程顯然是行不通的,——不管是從系統資源開銷方面或是響應用戶請求的效率方面來看。所以,操做系統中線程的概念便被引進了。線程,是進程的一部分,一個沒有線程的進程能夠被看做是單線程的。線程有時又被稱爲輕權進程或輕量級進程,也是 CPU 調度的一個基本單位。程序員
線程的建立有三種方式:繼承Thread,實現Runnable接口,利用Callable跟Futuregithub
(1)定義Thread類的子類,並重寫該類的run方法,該run方法的方法體就表明了線程要完成的任務。所以把run()方法稱爲執行體。 (2)建立Thread子類的實例,即建立了線程對象。 (3)調用線程對象的start()方法來啓動該線程。編程
public class FirstMethod extends Thread {
@Override
public void run() {
super.run();
}
}
FirstMethod firstMethod = new FirstMethod();
firstMethod.start();
複製代碼
public class SecondMethod implements Runnable{
@Override
public void run() {
}
}
SecondMethod secondMethod=new SecondMethod();
new Thread(secondMethod).start();
複製代碼
1)建立Callable接口的實現類,並實現call()方法,該call()方法將做爲線程執行體,而且有返回值。 (2)建立Callable實現類的實例,使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了該Callable對象的call()方法的返回值。 (3)使用FutureTask對象做爲Thread對象的target建立並啓動新線程。 (4)調用FutureTask對象的get()方法來得到子線程執行結束後的返回值數組
public class ThirdMethod implements Callable<String> {
@Override
public String call() throws Exception {
return Thread.currentThread().getName();
}
}
ThirdMethod thirdMethod=new ThirdMethod();
FutureTask<String> futureTask=new FutureTask<String>(thirdMethod);
try {
String threadName = futureTask.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
複製代碼
實現Runnable和實現Callable接口的方式基本相同,不過是後者執行call()方法有返回值,後者線程執行體run()方法無返回值,所以能夠把這兩種方式歸爲一種這種方式與繼承Thread類的方法之間的差異以下:bash
一、接口建立線程能夠實現資源共享,好比多個線程能夠共享一個Runnable資源 二、可是編程稍微複雜,若是須要訪問當前線程,必須調用Thread.currentThread()方法。 三、接口建立線能夠避免因爲Java的單繼承特性而帶來的侷限。服務器
如今經過一個程序員改Bug的例子來描述一下,一共有15個bug,如今安排3個程序員去Debug:網絡
經過Thread來實現併發
public class BugThread extends Thread {
private volatile int bugNumber = 5;
@Override
public void run() {
for (int i = 0; i < bugNumber; i++) {
System.out.println("bugNumber--->" + bugNumber--);
}
}
}
public class Main {
public static void main(String[] args) {
new BugThread().start();
new BugThread().start();
new BugThread().start();
}
}
複製代碼
運行結果:
Thread-0-----5
Thread-1-----5
Thread-2-----5
Thread-0-----4
Thread-2-----4
Thread-1-----4
Thread-2-----3
Thread-0-----3
Thread-2-----2
Thread-1-----3
Thread-2-----1
Thread-0-----2
Thread-0-----1
Thread-1-----2
Thread-1-----1
複製代碼
經過Runnable來實現
public class BugRunnable implements Runnable {
private volatile int bugNumber = 15;
@Override
public void run() {
while (bugNumber > 0)
System.out.println(Thread.currentThread().getName() + "-----" + bugNumber--);
}
}
public static void main(String[] args) {
BugRunnable bugRunnable = new BugRunnable();
new Thread(bugRunnable).start();
new Thread(bugRunnable).start();
new Thread(bugRunnable).start();
}
複製代碼
運行結果
Thread-0-----15
Thread-0-----14
Thread-0-----13
Thread-0-----12
Thread-1-----11
Thread-0-----10
Thread-1-----9
Thread-0-----8
Thread-1-----7
Thread-0-----6
Thread-1-----5
Thread-0-----4
Thread-1-----3
Thread-0-----2
Thread-1-----1
複製代碼
private volatile char name[];//線程名稱的字節數組
private int priority;//線程優先級
private boolean single_step; //線程是否單步
private boolean daemon = false; //是不是守護線程
private boolean stillborn = false; //JVM state
private Runnable target; //從構造方法傳過來的Runnable
private ThreadGroup group; //線程組
private ClassLoader contextClassLoader; //類加載器
private static int threadInitNumber; //線程編號
private volatile int threadStatus = 0; //初始狀態
public final static int MIN_PRIORITY = 1; //最低優先級
public final static int NORM_PRIORITY = 5; //默認優先級
public final static int MAX_PRIORITY = 10; //最高優先級
複製代碼
public enum State {
//Thread state for a thread which has not yet started.
NEW,
//Thread state for a runnable thread.
RUNNABLE,
//Thread state for a thread blocked waiting for a monitor lock.
BLOCKED,
// Thread state for a waiting thread.
WAITING,
//Thread state for a waiting thread with a specified waiting time.
TIMED_WAITING,
//Thread state for a terminated thread
TERMINATED;
}
複製代碼
線程的狀態有NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED,能夠整理成以下表格
線程狀態 | 解釋 |
---|---|
New | 還未調用 start() 方法 |
RUNNABLE | 調用了 start() ,此時線程已經準備好被執行,處於就緒隊列 |
BLOCKED | 線程阻塞於鎖或者調用了 sleep |
WAITING | 線程因爲某種緣由等待其餘線程 |
TIME_WAITING | 與 WAITING 的區別是能夠在特定時間後自動返回 |
TERMINATED | 執行完畢或者被其餘線程殺死 |
/** * Initializes a Thread. * * @param g //線程組 * @param target //構造方法傳過來的Runnable * @param name //線程名稱 * @param stackSize //給線程分配的棧的深度 * @param acc //上下文加載器 */
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name.toCharArray();
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
//判斷線程組參數是否爲空
if (g == null) {
if (security != null) {
g = security.getThreadGroup();
}
if (g == null) {
g = parent.getThreadGroup();
}
}
g.checkAccess();
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;//初始化線程組
this.daemon = parent.isDaemon();//定義是否爲守護線程
this.priority = parent.getPriority();//設置優先級
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;//初始化target
setPriority(priority);//設置優先級
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;//設置棧深度
/* Set thread ID */
tid = nextThreadID();//設置線程ID
}
複製代碼
public synchronized void start() {
if (threadStatus != 0)//判斷線程是否準備好
group.add(this);//將啓動的線程線程組
boolean started = false;
try {
start0();//本地方法,JVM調用target的run方法
started = true;//更改啓動標誌
} finally {
try {
if (!started)
group.threadStartFailed(this);//通知線程組啓動失敗
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then it will be passed up the call stack */
}
}
}
複製代碼
@Override
public void run() {
if (target != null) {
target.run();
}
}
複製代碼
synchronized 關鍵字說明start方法是同步的,而且是啓動這個線程進行執行,JVM將會調用這個線程的run方法,這樣產生的結果是,兩個線程執行着,其中一個是調用start()方法的線程執行,另外一個線程是執行run方法的線程。
public static void sleep(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
sleep(millis);//調用本地方法
}
複製代碼
線程休眠一段時間,讓其餘線程有機會繼續執行,須要捕捉異常。
public static native void yield();
複製代碼
public final synchronized void join(long millis) throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
複製代碼
join方法是等待該線程執行,直到超時或者終止,能夠做爲線程通訊的一種方式,A線程調用B線程的join(阻塞),等待B完成後再往下執行。
yield跟join
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();//檢查權限
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0();
b.interrupt(this);
return;
}
}
interrupt0();
}
複製代碼
interrupt()方法是中斷當前的線程, 此外還有isInterrupt,以及interrupted方法
前面說過,Java中的線程在底層是經過共享內存進行通訊的,在應用層則是經過調用Object對象的wait()方法和notify()方法或notifyAll()方法來實現線程間的通訊。 Object是全部類的超類,主要經過:notify()、notifyAll()、wait()、wait(long)和wait(long,int)這幾個方法來進行線程間通訊。
一、wait()
public final void wait() throws InterruptedException,IllegalMonitorStateException
複製代碼
二、notify()
public final native void notify() throws IllegalMonitorStateException
複製代碼
三、notifyAll()
public final native void notifyAll() throws IllegalMonitorStateException
複製代碼
wait()和sleep()的區別
經過對線程源碼的簡單分析,能夠看出線程也是有本身的生命週期的,可是因爲源碼中有不少native方法,致使了很難追蹤源碼,因此只能大體理解一下線程的各類狀態以及通訊過程,下面能夠經過一副流程圖來總結一下: