- 線程狀態
瞭解線程的狀態是基礎,理解其他內容更方便。
- 線程常用方法
注意wait/notify必須在同步代碼塊中執行;注意interrupt方法只是設置中斷位;
這些方法對新手來說一定要在實際操作中去體會細節。
- Thread相關類和示例
和thread相關的類有Runnable、Callable、Future、FutureTask等(簡單的概念)
線程狀態
- 新建狀態(New):新建線程對象
- 就緒狀態(Runnable) 調用start()方法
- 運行狀態(Running) 獲得cpu的執行權限
- (有人將阻塞分爲三種,等待阻塞、同步阻塞和其他阻塞;個人覺得不利於理解線程的狀態)
- BLOCKED/WAITING/TIMED_WAITING
- BLOCKED: 同步代碼塊/等待lock
- WAITING:無限等待喚醒、沒有超時時間的wait和join方法/locksupport.park()
- TIMED_WAITING: 有時限的等待、包括sleep、wait(ts)、join(ts)、locksupport.parkNanos/parkUntil
- 死亡狀態(Dead) 線程執行完成或者異常退出
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
線程常用方法
// 睡眠,不釋放控制權
TimeUnit.MINUTES.sleep(2);
//isAlive():線程處於“新建”狀態時,線程調用isAlive()方法返回false。
//在線程的run()方法結束之前,即沒有進入死亡狀態之前,線程調用isAlive()方法返回true.
// yield():調用此方法的線程釋放當前cpu的執行權、回到就緒狀態.
// join():在A線程中調用B線程join()方法,表示當執行到此方法時,直到B線程執行完畢
//(過期)suspend()和resume() 暫停和重新開始,釋放cpu執行權不釋放鎖;stop 已過期
//暫停和重開容易造成死鎖,Stop無法釋放資源
//wait() 和 notify() 等待和喚醒,釋放cpu執行權和鎖,Object方法
//wait/notify必須在同步代碼塊中執行,用於生成monitorenter 和 monitorexit指令
//同步塊對象鎖和執行wait的對象必須是同一個(否則IllegalMonitorStateException)
//監視器和對象鎖(深入理解jvm)通俗地講就是隻有獲取對obj的鎖定之後才能用後面代碼塊中的代碼訪問obj資源,否則就無法訪問obj也無法執行後面的代碼,只能讓線程停滯在那裏等待其它線程解除對obj的鎖定;
//interrupt 中斷sleep、join和wait三者之一的阻塞繼續執行。設置中斷標誌位爲true;線程還是會執行完
//提前結束等待(拋出對應的中斷異常InterruptedException: sleep interrupted)
//並不真正中斷正在運行的線程,而只是發出中斷請求,由線程在下一個合適的時刻中斷自己。中斷是取消線程最合理的方式。
Thread相關類和示例
Runnable、Callable、Future、FutureTask
繼承Thread就不多說了,用的比較少;Runnable看上面的例子
Callable和Runnable差不多,不過實現方法時call,而且有返回值,不能交給Thread來包裝運行
@Slf4j
public class RunnableDemo implements Runnable{
@Override
public void run() {
log.info("-------10秒後大軍來襲-----");
synchronized (this){
try {
this.wait(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.info("-------keep going-----");
}
public static void main(String[] args) {
Thread thread = new Thread(new RunnableDemo());
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Slf4j
public class CallableDemo implements Callable{
String callName;
public CallableDemo(String callName) {
this.callName = callName;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//創建一個線程池
ExecutorService pool = Executors.newFixedThreadPool(2);
//創建兩個有返回值的任務
Callable c1 = new CallableDemo("A");
Callable c2 = new CallableDemo("B");
//執行任務並獲取Future對象
Future f1 = pool.submit(c1);
Future f2 = pool.submit(c2);
//從Future對象上獲取任務的返回值,並輸出到控制檯
log.info(">>>"+ f1.get().toString());
log.info(">>>"+ f2.get().toString());
//關閉線程池
pool.shutdown();
}
@Override
public Object call() throws Exception {
log.info("-----執行call方法------");
return callName + ":success";
}
}
Future是線程池的結果(泛型)類,在線程池Executor通過submit返回結果是Future;
用來查詢Runnable或者Callable任務的執行情況,isDone,get等
FutureTask是一個類實現了RunnableFuture<V>,而RunnableFuture實現了Runnbale又實現了Futrue<V>這兩個接口,另外它還能包裝Runnale和Callable(通過適配器適配);
於是FutureTask既可以通過Thread包裝來運行又可以交給ExecuteService來運行。然後獲取任務執行情況
@Slf4j
public class FutureTaskDemo {
public static void main(String[] args) {
// futureTask 執行Callable
futureTaskTestCallRun();
//futureTaskTestCallAsync();
//futureTask 執行Runnable
//futureTaskTestRunnable();
}
// 異步的futureTask被取消
public static void futureTaskTestCallAsync(){
CallableDemo callableDemo = new CallableDemo("測試futureTask");
FutureTask futureTask1 = new FutureTask(callableDemo);
System.out.println("線程狀態:" + futureTask1.isDone());
Thread thread = new Thread(futureTask1);
thread.start();//異步
try {
System.out.println("cancel結果:" + futureTask1.cancel(true));
System.out.println("線程狀態:" + futureTask1.isDone());
thread.join();
System.out.println("執行結果:" + futureTask1.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
/**
* @Author JackZhou
* @Description 同步執行
**/
public static void futureTaskTestCallRun(){
CallableDemo callableDemo = new CallableDemo("測試futureTask");
FutureTask futureTask1 = new FutureTask(callableDemo);
System.out.println("線程狀態:" + futureTask1.isDone());
futureTask1.run();//同步
try {
System.out.println("cancel結果:" + futureTask1.cancel(true));
System.out.println("線程狀態:" + futureTask1.isDone());
System.out.println("執行結果:" + futureTask1.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
public static void futureTaskTestRunnable(){
RunnableDemo runnableDemo = new RunnableDemo();
FutureTask futureTask2 = new FutureTask(runnableDemo, Future.class);
Thread thread = new Thread(futureTask2);
thread.start();//異步
try {
System.out.println("線程狀態:" + futureTask2.isDone());
System.out.println("執行結果:" + futureTask2.get());
System.out.println("線程狀態:" + futureTask2.isDone());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
## futureTask state
cancel :取消方法,如圖所示,同步異步執行之前都可以被cancel,執行結束以後不許;
NEW:表示這是一個新的任務,或者還沒有執行完的任務,是初始狀態。
COMPLETING:表示任務執行結束(正常執行結束,或者發生異常結束),但是還沒有將結果保存到outcome中。是一箇中間狀態。
NORMAL:表示任務正常執行結束,並且已經把執行結果保存到outcome字段中。是一個最終狀態。
EXCEPTIONAL:表示任務發生異常結束,異常信息已經保存到outcome中,這是一個最終狀態。
CANCELLED:任務在新建之後,執行結束之前被取消了,但是不要求中斷正在執行的線程,也就是調用了cancel(false),任務就是CANCELLED狀態,這時任務狀態變化是NEW -> CANCELLED。
INTERRUPTING:任務在新建之後,執行結束之前被取消了,並要求中斷線程的執行,也就是調用了cancel(true),這時任務狀態就是INTERRUPTING。這是一箇中間狀態。
INTERRUPTED:調用cancel(true)取消異步任務,會調用interrupt()中斷線程的執行,然後狀態會從INTERRUPTING變到INTERRUPTED。
Future也是執行的FutureTask的cancel方法;
FutureTask封裝了計算任務,無論是提交給Thread執行,或者線程池執行,調用的都是FutureTask的run()。
嘗試用cas修改FutureTask的運行狀態爲中斷中INTERRUPTING或者已取消CANCELLED
如果獲mayInterruptIfRunning爲true,修改狀態爲中斷INTERRUPTED
將當前線程狀態設爲已中斷
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
finishCompletion();
}
return true;
}
### 取消的方法主要在finishCompletion
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
done();
callable = null; // to reduce footprint
}