Java多線程①——線程知識梳理

      多線程文章目錄

  • 線程狀態

       瞭解線程的狀態是基礎,理解其他內容更方便。

  • 線程常用方法

    注意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
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章