Java線程小刀牛試

線程簡介

什麼是線程

現代操做系統調度的最小單元是線程,也叫輕量級進程(Light Weight Process),在一個進程裏能夠建立多個線程,這些線程都擁有各自的計數器、堆棧和局部變量等屬性,而且可以訪問共享的內存變量。html

線程生命週期

java.lang.Thread.State 中定義了 6 種不一樣的線程狀態,在給定的一個時刻,線程只能處於其中的一個狀態。java

如下是各狀態的說明,以及狀態間的聯繫:git

  • 開始(New) - 尚未調用 start() 方法的線程處於此狀態。
  • 可運行(Runnable) - 已經調用了 start() 方法的線程狀態。此狀態意味着,線程已經準備好了,一旦被線程調度器分配了 CPU 時間片,就能夠運行線程。
  • 阻塞(Blocked) - 阻塞狀態。線程阻塞的線程狀態等待監視器鎖定。處於阻塞狀態的線程正在等待監視器鎖定,以便在調用 Object.wait() 以後輸入同步塊/方法或從新輸入同步塊/方法。
  • 等待(Waiting) - 等待狀態。一個線程處於等待狀態,是因爲執行了 3 個方法中的任意方法:
    • Object.wait()
    • Thread.join()
    • LockSupport.park()
  • 定時等待(Timed waiting) - 等待指定時間的狀態。一個線程處於定時等待狀態,是因爲執行了如下方法中的任意方法:
    • Thread.sleep(sleeptime)
    • Object.wait(timeout)
    • Thread.join(timeout)
    • LockSupport.parkNanos(timeout)
    • LockSupport.parkUntil(timeout)
  • 終止(Terminated) - 線程 run() 方法執行結束,或者因異常退出了 run() 方法,則該線程結束生命週期。死亡的線程不可再次復生。

啓動和終止線程

構造線程

構造線程主要有三種方式程序員

  • 繼承 Thread 類
  • 實現 Runnable 接口
  • 實現 Callable 接口

繼承 Thread 類

經過繼承 Thread 類構造線程的步驟:github

  • 定義 Thread 類的子類,並重寫該類的 run() 方法,該 run() 方法的方法體就表明了線程要完成的任務。所以把 run() 方法稱爲執行體。
  • 建立 Thread 子類的實例,即建立了線程對象。
  • 調用線程對象的 start() 方法來啓動該線程。

示例:數據庫

public class ThreadDemo02 {

    public static void main(String[] args) {
        Thread02 mt1 = new Thread02("線程A "); // 實例化對象
        Thread02 mt2 = new Thread02("線程B "); // 實例化對象
        mt1.start(); // 調用線程主體
        mt2.start(); // 調用線程主體
    }

    static class Thread02 extends Thread {

        private int ticket = 5;

        Thread02(String name) {
            super(name);
        }

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                if (this.ticket > 0) {
                    System.out.println(this.getName() + " 賣票:ticket = " + ticket--);
                }
            }
        }
    }
}

實現 Runnable 接口

經過實現 Runnable 接口構造線程的步驟:安全

  • 定義 Runnable 接口的實現類,並重寫該接口的 run() 方法,該 run() 方法的方法體一樣是該線程的線程執行體。
  • 建立 Runnable 實現類的實例,並依此實例做爲 Thread 的 target 來建立 Thread 對象,該 Thread 對象纔是真正的線程對象。
  • 調用線程對象的 start() 方法來啓動該線程。

示例:網絡

public class RunnableDemo {

    public static void main(String[] args) {
        MyThread t = new MyThread("Runnable 線程"); // 實例化對象
        new Thread(t).run(); // 調用線程主體
        new Thread(t).run(); // 調用線程主體
        new Thread(t).run(); // 調用線程主體
    }

    static class MyThread implements Runnable {

        private int ticket = 5;
        private String name;

        MyThread(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                if (this.ticket > 0) {
                    System.out.println(this.name + " 賣票:ticket = " + ticket--);
                }
            }
        }
    }
}

實現 Callable 接口

經過實現 Callable 接口構造線程的步驟:併發

  • 建立 Callable 接口的實現類,並實現 call() 方法,該 call() 方法將做爲線程執行體,而且有返回值。
  • 建立 Callable 實現類的實例,使用 FutureTask 類來包裝 Callable 對象,該 FutureTask 對象封裝了該 Callable 對象的 call() 方法的返回值。
  • 使用 FutureTask 對象做爲 Thread 對象的 target 建立並啓動新線程。
  • 調用 FutureTask 對象的 get() 方法來得到子線程執行結束後的返回值。

示例:dom

public class CallableAndFutureDemo {

    public static void main(String[] args) {
        Callable<Integer> callable = () -> new Random().nextInt(100);
        FutureTask<Integer> future = new FutureTask<>(callable);
        new Thread(future).start();
        try {
            Thread.sleep(1000);// 可能作一些事情
            System.out.println(future.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

三種建立線程方式對比

  • 實現 Runnable 接口優於繼承 Thread 類,由於實現接口方式更便於擴展類。
  • 實現 Runnable 接口的線程沒有返回值;而實現 Callable 接口的線程有返回值

中斷線程

當一個線程運行時,另外一個線程能夠直接經過 interrupt() 方法中斷其運行狀態。

public class ThreadInterruptDemo {

    public static void main(String[] args) {
        MyThread mt = new MyThread(); // 實例化Runnable子類對象
        Thread t = new Thread(mt, "線程"); // 實例化Thread對象
        t.start(); // 啓動線程
        try {
            Thread.sleep(2000); // 線程休眠2秒
        } catch (InterruptedException e) {
            System.out.println("三、休眠被終止");
        }
        t.interrupt(); // 中斷線程執行
    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            System.out.println("一、進入run()方法");
            try {
                Thread.sleep(10000); // 線程休眠10秒
                System.out.println("二、已經完成了休眠");
            } catch (InterruptedException e) {
                System.out.println("三、休眠被終止");
                return; // 返回調用處
            }
            System.out.println("四、run()方法正常結束");
        }
    }
}

終止線程

Thread 中的 stop 方法有缺陷,已廢棄。

安全地終止線程有兩種方法:

  1. 中斷狀態是線程的一個標識位,而中斷操做是一種簡便的線程間交互方式,而這種交互方式最適合用來取消或中止任務。
  2. 還能夠利用一個 boolean 變量來控制是否須要中止任務並終止該線程。
public class ThreadStopDemo03 {

    public static void main(String[] args) throws Exception {
        MyTask one = new MyTask();
        Thread countThread = new Thread(one, "CountThread");
        countThread.start();
        // 睡眠1秒,main線程對CountThread進行中斷,使CountThread可以感知中斷而結束
        TimeUnit.SECONDS.sleep(1);
        countThread.interrupt();
        MyTask two = new MyTask();
        countThread = new Thread(two, "CountThread");
        countThread.start();
        // 睡眠1秒,main線程對Runner two進行取消,使CountThread可以感知on爲false而結束
        TimeUnit.SECONDS.sleep(1);
        two.cancel();
    }

    private static class MyTask implements Runnable {

        private long i;
        private volatile boolean on = true;

        @Override
        public void run() {
            while (on && !Thread.currentThread().isInterrupted()) {
                i++;
            }
            System.out.println("Count i = " + i);
        }

        void cancel() {
            on = false;
        }
    }
}

Thread 中的重要方法

  • run - 線程的執行實體。
  • start - 線程的啓動方法。
  • setNamegetName - 能夠經過 setName()、 getName() 來設置、獲取線程名稱。
  • setPrioritygetPriority - 在 Java 中,全部線程在運行前都會保持在就緒狀態,那麼此時,哪一個線程優先級高,哪一個線程就有可能被先執行。能夠經過 setPriority、getPriority 來設置、獲取線程優先級。
  • setDaemonisDaemon - 可使用 setDaemon() 方法設置線程爲守護線程;可使用 isDaemon() 方法判斷線程是否爲守護線程。
  • isAlive - 能夠經過 isAlive 來判斷線程是否啓動。
  • interrupt - 當一個線程運行時,另外一個線程能夠直接經過 interrupt() 方法中斷其運行狀態。
  • join - 使用 join() 方法讓一個線程強制運行,線程強制運行期間,其餘線程沒法運行,必須等待此線程完成以後才能夠繼續執行。
  • Thread.sleep - 使用 Thread.sleep() 方法便可實現休眠。
  • Thread.yield - 可使用 Thread.yield() 方法將一個線程的操做暫時讓給其餘線程執行。

設置/獲取線程名稱

在 Thread 類中能夠經過 setName()、 getName() 來設置、獲取線程名稱。

public class ThreadNameDemo {

    public static void main(String[] args) {
        MyThread mt = new MyThread(); // 實例化Runnable子類對象
        new Thread(mt).start(); // 系統自動設置線程名稱
        new Thread(mt, "線程-A").start(); // 手工設置線程名稱
        Thread t = new Thread(mt); // 手工設置線程名稱
        t.setName("線程-B");
        t.start();
    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getName() + "運行,i = " + i); // 取得當前線程的名字
            }
        }
    }
}

判斷線程是否啓動

在 Thread 類中能夠經過 isAlive() 來判斷線程是否啓動。

public class ThreadAliveDemo {

    public static void main(String[] args) {
        MyThread mt = new MyThread(); // 實例化Runnable子類對象
        Thread t = new Thread(mt, "線程"); // 實例化Thread對象
        System.out.println("線程開始執行以前 --> " + t.isAlive()); // 判斷是否啓動
        t.start(); // 啓動線程
        System.out.println("線程開始執行以後 --> " + t.isAlive()); // 判斷是否啓動
        for (int i = 0; i < 3; i++) {
            System.out.println(" main運行 --> " + i);
        }
        // 如下的輸出結果不肯定
        System.out.println("代碼執行以後 --> " + t.isAlive()); // 判斷是否啓動

    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getName() + "運行,i = " + i);
            }
        }
    }
}

守護線程

在 Java 程序中,只要前臺有一個線程在運行,則整個 Java 進程就不會消失,因此此時能夠設置一個守護線程,這樣即便 Java 進程結束了,此守護線程依然會繼續執行。可使用 setDaemon() 方法設置線程爲守護線程;可使用 isDaemon() 方法判斷線程是否爲守護線程。

public class ThreadDaemonDemo {

    public static void main(String[] args) {
        Thread t = new Thread(new MyThread(), "線程");
        t.setDaemon(true); // 此線程在後臺運行
        System.out.println("線程 t 是不是守護進程:" + t.isDaemon());
        t.start(); // 啓動線程
    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            while (true) {
                System.out.println(Thread.currentThread().getName() + "在運行。");
            }
        }
    }
}

設置/獲取線程優先級

在 Java 中,全部線程在運行前都會保持在就緒狀態,那麼此時,哪一個線程優先級高,哪一個線程就有可能被先執行。

public class ThreadPriorityDemo {

    public static void main(String[] args) {
        System.out.println("主方法的優先級:" + Thread.currentThread().getPriority());
        System.out.println("MAX_PRIORITY = " + Thread.MAX_PRIORITY);
        System.out.println("NORM_PRIORITY = " + Thread.NORM_PRIORITY);
        System.out.println("MIN_PRIORITY = " + Thread.MIN_PRIORITY);

        Thread t1 = new Thread(new MyThread(), "線程A"); // 實例化線程對象
        Thread t2 = new Thread(new MyThread(), "線程B"); // 實例化線程對象
        Thread t3 = new Thread(new MyThread(), "線程C"); // 實例化線程對象
        t1.setPriority(Thread.MIN_PRIORITY); // 優先級最低
        t2.setPriority(Thread.MAX_PRIORITY); // 優先級最低
        t3.setPriority(Thread.NORM_PRIORITY); // 優先級最低
        t1.start(); // 啓動線程
        t2.start(); // 啓動線程
        t3.start(); // 啓動線程
    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(500); // 線程休眠
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 取得當前線程的名字
                String out =
                    Thread.currentThread().getName() + ",優先級:" + Thread.currentThread().getPriority() + ",運行:i = " + i;
                System.out.println(out);
            }
        }
    }
}

線程間通訊

wait/notify/notifyAll

wait、notify、notifyAll 是 Object 類中的方法。

  • wait - 線程自動釋放其佔有的對象鎖,並等待 notify。
  • notify - 喚醒一個正在 wait 當前對象鎖的線程,並讓它拿到對象鎖。
  • notifyAll - 喚醒全部正在 wait 前對象鎖的線程。

生產者、消費者示例:

public class ThreadWaitNotifyDemo02 {

    private static final int QUEUE_SIZE = 10;
    private static final PriorityQueue<Integer> queue = new PriorityQueue<>(QUEUE_SIZE);

    public static void main(String[] args) {
        new Producer("生產者A").start();
        new Producer("生產者B").start();
        new Consumer("消費者A").start();
        new Consumer("消費者B").start();
    }

    static class Consumer extends Thread {

        Consumer(String name) {
            super(name);
        }

        @Override
        public void run() {
            while (true) {
                synchronized (queue) {
                    while (queue.size() == 0) {
                        try {
                            System.out.println("隊列空,等待數據");
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            queue.notifyAll();
                        }
                    }
                    queue.poll(); // 每次移走隊首元素
                    queue.notifyAll();
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " 從隊列取走一個元素,隊列當前有:" + queue.size() + "個元素");
                }
            }
        }
    }

    static class Producer extends Thread {

        Producer(String name) {
            super(name);
        }

        @Override
        public void run() {
            while (true) {
                synchronized (queue) {
                    while (queue.size() == QUEUE_SIZE) {
                        try {
                            System.out.println("隊列滿,等待有空餘空間");
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            queue.notifyAll();
                        }
                    }
                    queue.offer(1); // 每次插入一個元素
                    queue.notifyAll();
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " 向隊列取中插入一個元素,隊列當前有:" + queue.size() + "個元素");
                }
            }
        }
    }
}

線程的禮讓

在線程操做中,可使用 Thread.yield() 方法將一個線程的操做暫時讓給其餘線程執行。

public class ThreadYieldDemo {

    public static void main(String[] args) {
        MyThread t = new MyThread();
        new Thread(t, "線程A").start();
        new Thread(t, "線程B").start();
    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "運行,i = " + i);
                if (i == 2) {
                    System.out.print("線程禮讓:");
                    Thread.yield();
                }
            }
        }
    }
}

線程的強制執行

在線程操做中,可使用 join() 方法讓一個線程強制運行,線程強制運行期間,其餘線程沒法運行,必須等待此線程完成以後才能夠繼續執行。

public class ThreadJoinDemo {

    public static void main(String[] args) {
        MyThread mt = new MyThread(); // 實例化Runnable子類對象
        Thread t = new Thread(mt, "mythread"); // 實例化Thread對象
        t.start(); // 啓動線程
        for (int i = 0; i < 50; i++) {
            if (i > 10) {
                try {
                    t.join(); // 線程強制運行
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("Main 線程運行 --> " + i);
        }
    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            for (int i = 0; i < 50; i++) {
                System.out.println(Thread.currentThread().getName() + " 運行,i = " + i); // 取得當前線程的名字
            }
        }
    }
}

線程的休眠

直接使用 Thread.sleep() 方法便可實現休眠。

public class ThreadSleepDemo {

    public static void main(String[] args) {
        new Thread(new MyThread("線程A", 1000)).start();
        new Thread(new MyThread("線程A", 2000)).start();
        new Thread(new MyThread("線程A", 3000)).start();
    }

    static class MyThread implements Runnable {

        private String name;
        private int time;

        private MyThread(String name, int time) {
            this.name = name; // 設置線程名稱
            this.time = time; // 設置休眠時間
        }

        @Override
        public void run() {
            try {
                Thread.sleep(this.time); // 休眠指定的時間
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(this.name + "線程,休眠" + this.time + "毫秒。");
        }
    }
}

ThreadLocal

ThreadLocal,不少地方叫作線程本地變量,也有些地方叫作線程本地存儲,其實意思差很少。可能不少朋友都知道 ThreadLocal 爲變量在每一個線程中都建立了一個副本,那麼每一個線程能夠訪問本身內部的副本變量。

源碼

ThreadLocal 的主要方法:

public class ThreadLocal<T> {
    public T get() {}
	public void remove() {}
	public void set(T value) {}
	public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {}
}
  • get()方法是用來獲取 ThreadLocal 在當前線程中保存的變量副本。
  • set()用來設置當前線程中變量的副本。
  • remove()用來移除當前線程中變量的副本。
  • initialValue()是一個 protected 方法,通常是用來在使用時進行重寫的,它是一個延遲加載方法,下面會詳細說明。

get() 源碼實現

get 源碼

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}
  1. 取得當前線程。
  2. 經過 getMap() 方法獲取 ThreadLocalMap。
  3. 成功,返回 value;失敗,返回 setInitialValue()。

ThreadLocalMap 源碼實現

ThreadLocalMap 源碼

ThreadLocalMap 是 ThreadLocal 的一個內部類。

ThreadLocalMap 的 Entry 繼承了 WeakReference,而且使用 ThreadLocal 做爲鍵值。

setInitialValue 源碼實現

private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}

若是 map 不爲空,就設置鍵值對;爲空,再建立 Map,看一下 createMap 的實現:

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

ThreadLocal 源碼小結

至此,可能大部分朋友已經明白了 ThreadLocal 是如何爲每一個線程建立變量的副本的:

  1. 在每一個線程 Thread 內部有一個 ThreadLocal.ThreadLocalMap 類型的成員變量 threadLocals,這個 threadLocals 就是用來存儲實際的變量副本的,鍵值爲當前 ThreadLocal 變量,value 爲變量副本(即 T 類型的變量)。
  2. 在 Thread 裏面,threadLocals 爲空,當經過 ThreadLocal 變量調用 get()方法或者 set()方法,就會對 Thread 類中的 threadLocals 進行初始化,而且以當前 ThreadLocal 變量爲鍵值,以 ThreadLocal 要保存的副本變量爲 value,存到 threadLocals。
  3. 在當前線程裏面,若是要使用副本變量,就能夠經過 get 方法在 threadLocals 裏面查找。

示例

ThreadLocal 最多見的應用場景爲用於解決數據庫鏈接、Session 管理等問題。

示例 - 數據庫鏈接

private static ThreadLocal<Connection> connectionHolder
= new ThreadLocal<Connection>() {
public Connection initialValue() {
    return DriverManager.getConnection(DB_URL);
}
};

public static Connection getConnection() {
return connectionHolder.get();
}

示例 - Session 管理

private static final ThreadLocal threadSession = new ThreadLocal();

public static Session getSession() throws InfrastructureException {
    Session s = (Session) threadSession.get();
    try {
        if (s == null) {
            s = getSessionFactory().openSession();
            threadSession.set(s);
        }
    } catch (HibernateException ex) {
        throw new InfrastructureException(ex);
    }
    return s;
}

管道輸入/輸出流

管道輸入/輸出流和普通的文件輸入/輸出流或者網絡輸入/輸出流不一樣之處在於,它主要用於線程之間的數據傳輸,而傳輸的媒介爲內存。 管道輸入/輸出流主要包括了以下 4 種具體實現:PipedOutputStream、PipedInputStream、PipedReader 和 PipedWriter,前兩種面向字節,然後兩種面向字符。

public class Piped {

    public static void main(String[] args) throws Exception {
        PipedWriter out = new PipedWriter();
        PipedReader in = new PipedReader();
        // 將輸出流和輸入流進行鏈接,不然在使用時會拋出IOException
        out.connect(in);
        Thread printThread = new Thread(new Print(in), "PrintThread");
        printThread.start();
        int receive = 0;
        try {
            while ((receive = System.in.read()) != -1) {
                out.write(receive);
            }
        } finally {
            out.close();
        }
    }

    static class Print implements Runnable {

        private PipedReader in;

        Print(PipedReader in) {
            this.in = in;
        }

        public void run() {
            int receive = 0;
            try {
                while ((receive = in.read()) != -1) {
                    System.out.print((char) receive);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

FAQ

start() 和 run() 有什麼區別?能夠直接調用 Thread 類的 run() 方法麼?

run() 方法是線程的執行體。

start() 方法會啓動線程,而後 JVM 會讓這個線程去執行 run() 方法。

能夠直接調用 Thread 類的 run() 方法麼?

  • 能夠。可是若是直接調用 Thread 的 run()方法,它的行爲就會和普通的方法同樣。
  • 爲了在新的線程中執行咱們的代碼,必須使用 Thread.start()方法。

sleep()、yield()、join() 方法有什麼區別?爲何 sleep()和 yield()方法是靜態的?

  • yield()
    • yield() 方法可讓當前正在執行的線程暫停,但它不會阻塞該線程,它只是將該線程從 Running 狀態轉入 Runnable 狀態。
    • 當某個線程調用了 yield() 方法暫停以後,只有優先級與當前線程相同,或者優先級比當前線程更高的處於就緒狀態的線程纔會得到執行的機會。
  • sleep()
    • sleep() 方法須要指定等待的時間,它可讓當前正在執行的線程在指定的時間內暫停執行,進入 Blocked 狀態。
    • 該方法既可讓其餘同優先級或者高優先級的線程獲得執行的機會,也可讓低優先級的線程獲得執行機會。
    • 可是, sleep() 方法不會釋放「鎖標誌」,也就是說若是有 synchronized 同步塊,其餘線程仍然不能訪問共享數據。
  • join()
    • join() 方法會使當前線程轉入 Blocked 狀態,等待調用 join() 方法的線程結束後才能繼續執行。

參考閱讀:Java 線程中 yield 與 join 方法的區別 參考閱讀:sleep(),wait(),yield()和 join()方法的區別

爲何 sleep() 和 yield() 方法是靜態的

  • Thread 類的 sleep() 和 yield() 方法將處理 Running 狀態的線程。因此在其餘處於非 Running 狀態的線程上執行這兩個方法是沒有意義的。這就是爲何這些方法是靜態的。它們能夠在當前正在執行的線程中工做,並避免程序員錯誤的認爲能夠在其餘非運行線程調用這些方法。

Java 的線程優先級如何控制?高優先級的 Java 線程必定先執行嗎?

  • Java 中的線程優先級如何控制
    • Java 中的線程優先級的範圍是 [1,10],通常來講,高優先級的線程在運行時會具備優先權。能夠經過 thread.setPriority(Thread.MAX_PRIORITY) 的方式設置,默認優先級爲 5。
  • 高優先級的 Java 線程必定先執行嗎
    • 即便設置了線程的優先級,也沒法保證高優先級的線程必定先執行
    • 緣由:這是由於線程優先級依賴於操做系統的支持,然而,不一樣的操做系統支持的線程優先級並不相同,不能很好的和 Java 中線程優先級一一對應。
    • 結論:Java 線程優先級控制並不可靠。

什麼是守護線程?爲何要用守護線程?如何建立守護線程?

  • 什麼是守護線程
    • 守護線程(Daemon Thread)是在後臺執行而且不會阻止 JVM 終止的線程。
    • 與守護線程(Daemon Thread)相反的,叫用戶線程(User Thread),也就是非守護線程。
  • 爲何要用守護線程
    • 守護線程的優先級比較低,用於爲系統中的其它對象和線程提供服務。典型的應用就是垃圾回收器。
  • 如何建立守護線程
    • 使用 thread.setDaemon(true) 能夠設置 thread 線程爲守護線程。
    • 注意點:
      • 正在運行的用戶線程沒法設置爲守護線程,因此 thread.setDaemon(true) 必須在 thread.start() 以前設置,不然會拋出 llegalThreadStateException 異常;
      • 一個守護線程建立的子線程依然是守護線程。
      • 不要認爲全部的應用均可以分配給 Daemon 來進行服務,好比讀寫操做或者計算邏輯。

參考閱讀:Java 中守護線程的總結

爲何線程通訊的方法 wait(), notify()和 notifyAll()被定義在 Object 類裏?

Java 的每一個對象中都有一個鎖(monitor,也能夠成爲監視器) 而且 wait(),notify()等方法用於等待對象的鎖或者通知其餘線程對象的監視器可用。在 Java 的線程中並無可供任何對象使用的鎖和同步器。這就是爲何這些方法是 Object 類的一部分,這樣 Java 的每個類都有用於線程間通訊的基本方法

爲何 wait(), notify()和 notifyAll()必須在同步方法或者同步塊中被調用?

當一個線程須要調用對象的 wait()方法的時候,這個線程必須擁有該對象的鎖,接着它就會釋放這個對象鎖並進入等待狀態直到其餘線程調用這個對象上的 notify()方法。一樣的,當一個線程須要調用對象的 notify()方法時,它會釋放這個對象的鎖,以便其餘在等待的線程就能夠獲得這個對象鎖。因爲全部的這些方法都須要線程持有對象的鎖,這樣就只能經過同步來實現,因此他們只能在同步方法或者同步塊中被調用。

免費Java資料須要本身領取,涵蓋了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo/Kafka、Hadoop、Hbase、Flink等高併發分佈式、大數據、機器學習等技術。
傳送門: https://mp.weixin.qq.com/s/JzddfH-7yNudmkjT0IRL8Q
相關文章
相關標籤/搜索