Java基礎16:Java多線程基礎最全總結

更多內容請關注微信公衆號【Java技術江湖】java

這是一位阿里 Java 工程師的技術小站,做者黃小斜,專一 Java 相關技術:SSM、SpringBoot、MySQL、分佈式、中間件、集羣、Linux、網絡、多線程,偶爾講點Docker、ELK,同時也分享技術乾貨和學習經驗,致力於Java全棧開發!(關注公衆號後回覆」資料「便可領取 3T 免費技術學習資源以及我我原創的程序員校招指南、Java學習指南等資源)
git

本文介紹了Java多線程的基本概念,使用方法,以及底層實現原理。幫助你更好地使用Java的多線程。程序員

具體代碼在個人GitHub中能夠找到github

https://github.com/h2pl/MyTech

喜歡的話麻煩點一下星哈謝謝。編程

文章首發於個人我的博客:後端

https://h2pl.github.io/2018/0...

更多關於Java後端學習的內容請到個人CSDN博客上查看:安全

https://blog.csdn.net/a724888

Java中的線程

Java之父對線程的定義是:微信

線程是一個獨立執行的調用序列,同一個進程的線程在同一時刻共享一些系統資源(好比文件句柄等)也能訪問同一個進程所建立的對象資源(內存資源)。java.lang.Thread對象負責統計和控制這種行爲。

每一個程序都至少擁有一個線程-即做爲Java虛擬機(JVM)啓動參數運行在主類main方法的線程。在Java虛擬機初始化過程當中也可能啓動其餘的後臺線程。這種線程的數目和種類因JVM的實現而異。然而全部用戶級線程都是顯式被構造並在主線程或者是其餘用戶線程中被啓動。網絡

本文主要講了java中多線程的使用方法、線程同步、線程數據傳遞、線程狀態及相應的一些線程函數用法、概述等。在這以前,首先讓咱們來了解下在操做系統中進程和線程的區別:

  進程:每一個進程都有獨立的代碼和數據空間(進程上下文),進程間的切換會有較大的開銷,一個進程包含1--n個線程。(進程是資源分配的最小單位)

  線程:同一類線程共享代碼和數據空間,每一個線程有獨立的運行棧和程序計數器(PC),線程切換開銷小。(線程是cpu調度的最小單位)

  線程和進程同樣分爲五個階段:建立、就緒、運行、阻塞、終止。

  多進程是指操做系統能同時運行多個任務(程序)。

  多線程是指在同一程序中有多個順序流在執行。

在java中要想實現多線程,有兩種手段,一種是繼續Thread類,另一種是實現Runable接口.(其實準確來說,應該有三種,還有一種是實現Callable接口,並與Future、線程池結合使用

Java線程內存模型

下面的圖大體介紹了Java線程的調用過程,每一個線程使用一個獨立的調用棧進行線程執行,棧中的數據不共享,堆區和方法區的數據是共享的。
image多線程

image

image

構造方法和守護線程

構造方法
Thread類中不一樣的構造方法接受以下參數的不一樣組合:

一個Runnable對象,這種狀況下,Thread.start方法將會調用對應Runnable對象的run方法。若是沒有提供Runnable對象,那麼就會當即獲得一個Thread.run的默認實現。

一個做爲線程標識名的String字符串,該標識在跟蹤和調試過程當中會很是有用,除此別無它用。

線程組(ThreadGroup),用來放置新建立的線程,若是提供的ThreadGroup不容許被訪問,那麼就會拋出一個SecurityException 。



Thread對象擁有一個守護(daemon)標識屬性,這個屬性沒法在構造方法中被賦值,可是能夠在線程啓動以前設置該屬性(經過setDaemon方法)。

當程序中全部的非守護線程都已經終止,調用setDaemon方法可能會致使虛擬機粗暴的終止線程並退出。

isDaemon方法可以返回該屬性的值。守護狀態的做用很是有限,即便是後臺線程在程序退出的時候也常常須要作一些清理工做。

(daemon的發音爲」day-mon」,這是系統編程傳統的遺留,系統守護進程是一個持續運行的進程,好比打印機隊列管理,它老是在系統中運行。)

啓動線程的方式和isAlive方法

啓動線程
調用start方法會觸發Thread實例以一個新的線程啓動其run方法。新線程不會持有調用線程的任何同步鎖。

當一個線程正常地運行結束或者拋出某種未檢測的異常(好比,運行時異常(RuntimeException),錯誤(ERROR) 或者其子類)線程就會終止。

當線程終止以後,是不能被從新啓動的。在同一個Thread上調用屢次start方法會拋出InvalidThreadStateException異常。

若是線程已經啓動可是尚未終止,那麼調用isAlive方法就會返回true.即便線程因爲某些緣由處於阻塞(Blocked)狀態該方法依然返回true。

若是線程已經被取消(cancelled),那麼調用其isAlive在何時返回false就因各Java虛擬機的實現而異了。沒有方法能夠得知一個處於非活動狀態的線程是否已經被啓動過了。

優先級

Java的線程實現基本上都是內核級線程的實現,因此Java線程的具體執行還取決於操做系統的特性。

Java虛擬機爲了實現跨平臺(不一樣的硬件平臺和各類操做系統)的特性,Java語言在線程調度與調度公平性上未做出任何的承諾,甚至都不會嚴格保證線程會被執行。可是Java線程卻支持優先級的方法,這些方法會影響線程的調度:

每一個線程都有一個優先級,分佈在Thread.MIN_PRIORITY和Thread.MAX_PRIORITY之間(分別爲1和10)
默認狀況下,新建立的線程都擁有和建立它的線程相同的優先級。main方法所關聯的初始化線程擁有一個默認的優先級,這個優先級是Thread.NORM_PRIORITY (5).

線程的當前優先級能夠經過getPriority方法得到。
線程的優先級能夠經過setPriority方法來動態的修改,一個線程的最高優先級由其所在的線程組限定。

線程的控制方法

只有不多幾個方法能夠用於跨線程交流:

每一個線程都有一個相關的Boolean類型的中斷標識。在線程t上調用t.interrupt會將該線程的中斷標識設爲true,除非線程t正處於Object.wait,Thread.sleep,或者Thread.join,這些狀況下interrupt調用會致使t上的這些操做拋出InterruptedException異常,可是t的中斷標識會被設爲false。

任何一個線程的中斷狀態均可以經過調用isInterrupted方法來獲得。若是線程已經經過interrupt方法被中斷,這個方法將會返回true。

可是若是調用了Thread.interrupted方法且中斷標識尚未被重置,或者是線程處於wait,sleep,join過程當中,調用isInterrupted方法將會拋出InterruptedException異常。

調用t.join()方法將會暫停執行調用線程,直到線程t執行完畢:當t.isAlive()方法返回false的時候調用t.join()將會直接返回(return)。

另外一個帶參數毫秒(millisecond)的join方法在被調用時,若是線程沒可以在指定的時間內完成,調用線程將從新獲得控制權。

由於isAlive方法的實現原理,因此在一個尚未啓動的線程上調用join方法是沒有任何意義的。一樣的,試圖在一個尚未建立的線程上調用join方法也是不明智的。

起初,Thread類還支持一些另一些控制方法:suspend,resume,stop以及destroy。這幾個方法已經被聲明過時。其中destroy方法歷來沒有被實現,估計之後也不會。而經過使用等待/喚醒機制增長suspend和resume方法在安全性和可靠性的效果有所欠缺

Thread的靜態方法

靜態方法
Thread類中的部分方法被設計爲只適用於當前正在運行的線程(即調用Thread方法的線程)。爲強調這點,這些方法都被聲明爲靜態的。

Thread.currentThread方法會返回當前線程的引用,獲得這個引用能夠用來調用其餘的非靜態方法,好比Thread.currentThread().getPriority()會返回調用線程的優先級。

Thread.interrupted方法會清除當前線程的中斷狀態並返回前一個狀態。(一個線程的中斷狀態是不容許被其餘線程清除的)

Thread.sleep(long msecs)方法會使得當前線程暫停執行至少msecs毫秒。

Thread.yield方法純粹只是建議Java虛擬機對其餘已經處於就緒狀態的線程(若是有的話)調度執行,而不是當前線程。最終Java虛擬機如何去實現這種行爲就徹底看其喜愛了。

線程組

每個線程都是一個線程組中的成員。默認狀況下,新建線程和建立它的線程屬於同一個線程組。線程組是以樹狀分佈的。

當建立一個新的線程組,這個線程組成爲當前線程組的子組。getThreadGroup方法會返回當前線程所屬的線程組,對應地,ThreadGroup類也有方法能夠獲得哪些線程目前屬於這個線程組,好比enumerate方法。

ThreadGroup類存在的一個目的是支持安全策略來動態的限制對該組的線程操做。好比對不屬於同一組的線程調用interrupt是不合法的。

這是爲避免某些問題(好比,一個applet線程嘗試殺掉主屏幕的刷新線程)所採起的措施。ThreadGroup也能夠爲該組全部線程設置一個最大的線程優先級。

線程組每每不會直接在程序中被使用。在大多數的應用中,若是僅僅是爲在程序中跟蹤線程對象的分組,那麼普通的集合類(好比java.util.Vector)應是更好的選擇。

多線程的實現

public class 多線程實例 {

    //繼承thread
    @Test
    public void test1() {
        class A extends Thread {
            @Override
            public void run() {
                System.out.println("A run");
            }
        }
        A a = new A();
        a.start();
    }

    //實現Runnable
    @Test
    public void test2() {
        class B implements Runnable {

            @Override
            public void run() {
                System.out.println("B run");
            }
        }
        B b = new B();
        //Runable實現類須要由Thread類包裝後才能執行
        new Thread(b).start();
    }

    //有返回值的線程
    @Test
    public void test3() {
        Callable callable = new Callable() {
            int sum = 0;
            @Override
            public Object call() throws Exception {
                for (int i = 0;i < 5;i ++) {
                    sum += i;
                }
                return sum;
            }
        };
        //這裏要用FutureTask,不然不能加入Thread構造方法
        FutureTask futureTask = new FutureTask(callable);
        new Thread(futureTask).start();
        try {
            System.out.println(futureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    //線程池實現
    @Test
    public void test4() {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        //execute直接執行線程
        executorService.execute(new Thread());
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("runnable");
            }
        });
        //submit提交有返回結果的任務,運行完後返回結果。
        Future future = executorService.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return "a";
            }
        });
        try {
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        ArrayList<String> list = new ArrayList<>();
        //有返回值的線程組將返回值存進集合
        for (int i = 0;i < 5;i ++ ) {
            int finalI = i;
            Future future1 = executorService.submit(new Callable<String>() {
                @Override
                public String call() throws Exception {
                    return "res" + finalI;
                }
            });
            try {
                list.add((String) future1.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        for (String s : list) {
            System.out.println(s);
        }
    }
}

線程狀態轉換

public class 線程的狀態轉換 {
//一開始線程是init狀態,結束時是terminated狀態
class t implements Runnable {
    private String name;
    public t(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        System.out.println(name + "run");
    }
}

//測試join,父線程在子線程運行時進入waiting狀態
@Test
public void test1() throws InterruptedException {
    Thread dad = new Thread(new Runnable() {
        Thread son = new Thread(new t("son"));
        @Override
        public void run() {
            System.out.println("dad init");
            son.start();
            try {
                //保證子線程運行完再運行父線程
                son.join();
                System.out.println("dad run");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    //調用start,線程進入runnable狀態,等待系統調度
    dad.start();
    //在父線程中對子線程實例使用join,保證子線程在父線程以前執行完

}

//測試sleep
@Test
public void test2(){
    Thread t1 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("t1 run");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });

    //主線程休眠。進入time waiting狀態
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    t1.start();

}

//線程2進入blocked狀態。
public static void main(String[] args) {
    test4();
    Thread.yield();//進入runnable狀態
}

//測試blocked狀態
public static void test4() {
    class A {
        //線程1得到實例鎖之後線程2沒法得到實例鎖,因此進入blocked狀態
        synchronized void run() {
            while (true) {
                System.out.println("run");
            }
        }
    }
    A a = new A();
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("t1 get lock");
            a.run();
        }
    }).start();
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("t2 get lock");
            a.run();
        }
    }).start();

}

//volatile保證線程可見性
volatile static int flag = 1;
//object做爲鎖對象,用於線程使用wait和notify方法
volatile static Object o = new Object();
//測試wait和notify
//wait後進入waiting狀態,被notify進入blocked(阻塞等待鎖釋放)或者runnable狀態(獲取到鎖)
public void test5() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            //wait和notify只能在同步代碼塊內使用
            synchronized (o) {
                while (true) {
                    if (flag == 0) {
                        try {
                            Thread.sleep(2000);
                            System.out.println("thread1 wait");
                            //釋放鎖,線程掛起進入object的等待隊列,後續代碼運行
                            o.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("thread1 run");
                    System.out.println("notify t2");
                    flag = 0;
                    //通知等待隊列的一個線程獲取鎖
                    o.notify();
                }
            }
        }
    }).start();
    //解釋同上
    new Thread(new Runnable() {
        @Override
        public void run() {
            while (true) {
                synchronized (o) {
                    if (flag == 1) {
                        try {
                            Thread.sleep(2000);
                            System.out.println("thread2 wait");
                            o.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("thread2 run");
                    System.out.println("notify t1");
                    flag = 1;
                    o.notify();
                }
            }
        }
    }).start();
}

//輸出結果是
//    thread1 run
//    notify t2
//    thread1 wait
//    thread2 run
//    notify t1
//    thread2 wait
//    thread1 run
//    notify t2
//不斷循環

}
相關文章
相關標籤/搜索