91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

掌握之并發編程-2.線程

發布時間:2020-07-17 04:27:59 來源:網絡 閱讀:147 作者:學習Lr 欄目:編程語言

掌握高并發、高可用架構

第二課 并發編程

從本課開始學習并發編程的內容。主要介紹并發編程的基礎知識、鎖、內存模型、線程池、各種并發容器的使用。

第二節 線程

并發編程 并發基礎 進程 線程 線程通信

上一節學習了進程和線程的關系,CPU和線程的關系。在程序開發過程中,最主要的還是線程,畢竟它是用來執行任務的。所以就需要知道,如何啟動和停止線程;線程的狀態;線程間如何通信。

線程的啟動
  1. 實現Runnable接口,然后當成Thread的構造參數生成線程對象,調用t.start()方法
public class MyThread implements Runnable {
    @Override
    public void run() {
        System.out.println("thread02");
    }

    public static void main(String[] args) {
        Thread t = new Thread(new MyThread());
        t.start();
    }
}

這是線程最本質的實現。Thread類實現了Runnable接口,在執行t.start()時,會調用Threadrun()方法,從而間接調用target.run()

Thread類實現Runnalbe接口:

public class Thread implements Runnable {
    private Runnable target;
    public void run() {
        if (target != null) {
            target.run();
        }
    }
}

2 繼承Thread類,然后調用start()方法

public class MyThread extends Thread {
    public void run() {
        System.out.println("thread01");
    }
    public static void main(String[] args) {
        MyThread t = new MyThread();
        t.start();
    }
}

由于Thread實現了Runnable,所以繼承Thread來重寫run()方法的本質依然是實現Runnable接口的定義。此時,由于target對象為null,所以Threadrun()方法不會執行target.run(),而是直接執行自定義的run()方法。

3 實現Callable接口,并通過FutureTask

public class MyCallable implements Callable<String> {

    @Override
    public String call() throws Exception {
        return null;
    }

    public static void main(String[] args) {
        MyCallable m = new MyCallable();
        FutureTask<String> f = new FutureTask<>(m);
        Thread t = new Thread(f);
        t.start();

        String result = f.get(); // 同步獲取任務執行結果
        System.out.println(result);
    }
}

由于FutureTask實現了RunnableTask接口,而RunnableTask又實現了RunnableFuture接口,因此在構造Thread時,FutureTask還是被轉型為Runnable來使用了。

前兩種方法只能執行任務,而不能得到任務的結果;第三種方法可以通過FutureTaskget()方法同步的獲取任務結果。當任務執行中時,其會阻塞直到任務完成。

4 匿名內部類

public class DemoThread {

    public static void main(String[] args) {
        new Thread() {
            @Override
            public void run() {
                //...
            }
        }.start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                //...
            }
        }).start();
    }
}

5 Lambda表達式

public class Demo {

    public static void main(String[] args) {
        new Thread(() -> System.out.println("running")).start();
    }
}

6 線程池

public class MyThreadPool implements Runnable {
    @Override
    public void run() {
        // TODO
    }

    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();

        MyThreadPool m = new MyThreadPool();
        exec.execute(m);
    }
}

把任務的執行交給ExecutorService去處理,最終還是利用Thread創建線程。優點是線程的復用,省去了每個線程的創建和銷毀過程,從而提高效率。

7 定時器

public class MyTimer {

    public static void main(String[] args) {
        Timer t = new Timer();

        t.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                // TODO
            }
        }, 2000, 1000);
    }
}

TimerTask實現了Runnable接口,Timer內部有個TimerThread繼承了Thread,所以還是Thread+Runnable

線程的停止
  1. 當線程的run方法執行完成后,線程自動釋放資源進而終止。
  2. 在另外的線程中調用interrupt來中斷某個線程。這是線程間通信,我們后續再講
線程的狀態

先上圖(借用CSDN博主 潘建南 的圖)。

掌握之并發編程-2.線程

所以,線程的狀態一共有6種。下面咱們來詳細講解。

  1. 初始狀態 NEW

通過實現Runnable或繼承Thread得到一個線程類,并使用new創建出一個線程對象,就進入了初始狀態。此時,還未調用start方法。

  1. 運行狀態 RUNNABLE

JAVA中將 就緒(READY)和 運行中(RUNNING)兩種狀態統稱為“運行”狀態。

就緒 READY:就是說線程有資格運行,但此時調度程序還未選擇線程。當以下行為發生時,線程進入就緒狀態。

  • 調用線程的start方法
  • 當前線程的sleep結束
  • 其他線程join結束
  • 等待用戶輸入,但用戶輸入完畢
  • 線程拿到對象鎖
  • 當前線程的時間片用完了
  • 調用當前線程的yield方法

運行中 RUNNING:調度程序從就緒的線程池中選擇一個線程使其成為當前線程,此時線程處于的狀態就是運行中。

  1. 阻塞 BLOCKED

阻塞狀態是線程在獲取對象的同步鎖synchorized時,因為該鎖被其他線程占用而放棄CPU使用權,暫時停止運行的狀態。此時的線程會被JVM放入鎖池中。

  1. 等待 WAITING

運行的線程執行wait()方法,會釋放線程占用的所有資源,并進入等待池中。此時,線程是不能自動喚醒的,必須依靠其他線程調用notify()notifyAll()方法才能喚醒。

  1. 超時等待 TIMED_WAITING

運行的線程執行sleep()join()方法,或者發出I/O請求時的狀態。此時線程會放棄CPU使用權。當sleep()超時、join()等待線程終止或超時、I/O處理完畢時,重新轉入就緒。

  1. 終止 TERMINATED

線程執行完成或因異常而退出run方法體的狀態。

線程各個狀態之間的跳轉,可以仔細看圖。

線程間通信
  1. 通過共享變量通信

在共享對象的變量中設置信號量。線程A修改信號量的值,線程B根據信號量來做不同的處理。

  1. 通過wait()notify()notifyAll()來通信

JAVA要求wait()notify()notifyAll()必須在同步代碼塊中使用。就是說,必須要獲得對象鎖。所以wait()notify()notifyAll()經常和sychronized搭配使用。

執行了鎖定對象的wait()方法后,當前線程會釋放獲得的對象鎖,進入鎖定對象的等待池

在執行同步代碼塊的過程中,如果調用Thread.sleep()Thread.yield(),當前線程只是放棄CPU,并不會釋放對象鎖

JOIN

作用:讓 主線程 等待 子線程 執行完成再繼續運行。

// 主線程
public class Father extends Thread {
    public void run() {
        Son son = new Son();
        son.start();
        son.join();
        ...
    }
}

// 子線程
public class Son extends Thread {
    public void run() {
        ...
    }
}

在Father主線程中,先啟動Son子線程,然后調用son.join(),此時,Father主線程會一直等待,直到子線程執行完成,才能繼續運行。

分析源碼可以知道,JOIN的實現原理是:只要子線程是活動的,就一直觸發主線程的wait()方法,使其一直處于等待狀態。

public final void join() throws InterruptedException {
    join(0);
}

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;
        }
    }
}
yield

調用yield()方法,意思是放棄CPU使用權,回到就緒狀態。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

台山市| 南部县| 辉县市| 筠连县| 洛扎县| 永丰县| 泰顺县| 竹北市| 福泉市| 巴东县| 庆安县| 武宣县| 嘉荫县| 武隆县| 车致| 兴仁县| 大城县| 平顶山市| 涞水县| 溧水县| 宜州市| 白水县| 临西县| 磴口县| 普陀区| 高碑店市| 博客| 新河县| 牟定县| 富民县| 丹阳市| 霍城县| 当雄县| 缙云县| 河间市| 城固县| 福鼎市| 司法| 平果县| 清水河县| 肇庆市|