詳解Thread多線程

如何建立線程

Java 中建立線程的方法有三種:程序員

1. 繼承 Thread 類建立線程面試

新建一個類繼承 Thread 類,並重寫 Thread 類的 run() 方法。
建立 Thread 子類的實例。
調用該子類實例的 start() 方法啓動該線程。多線程

代碼舉例以下:架構

public class HelloThread1 {
    static class ThreadDemo extends Thread {
        @Override
        public void run() {
            System.out.println("Hello Thread");
        }
    }
    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo();
        threadDemo.start();
    }
}

2.實現 Runnable 接口建立線程:併發

建立一個類實現 Runnable 接口,並重寫該接口的 run() 方法。
建立該實現類的實例。
將該實例傳入 Thread(Runnable r) 構造方法中建立 Thread 實例。
調用該 Thread 線程對象的 start() 方法。分佈式

代碼舉例以下:ide

public class HelloThread1 {
    static class ThreadDemo extends Thread {
        @Override
        public void run() {
            System.out.println("Hello Thread");
        }
    }
    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo();
        threadDemo.start();
    }
}

3. 使用 Callable 和 FutureTask 建立線程:高併發

建立一個類實現 Callable 接口,並重寫 call() 方法。
建立該 Callable 接口實現類的實例。
將 Callable 的實現類實例傳入 FutureTask(Callable<V> callable) 構造方法中建立 FutureTask 實例。
將 FutureTask 實例傳入 Thread(Runnable r) 構造方法中建立 Thread 實例。
調用該 Thread 線程對象的 start() 方法。
調用 FutureTask 實例對象的 get() 方法獲取返回值。學習

代碼舉例以下:測試

public class HelloThread3 {
    static class ThreadDemo implements Callable<String> {
        @Override
        public String call() {
            System.out.println("Hello Thread");
            return "Callable return value";
        }
    }
    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo();
        FutureTask<String> futureTask = new FutureTask<String>(threadDemo);
        Thread thread = new Thread(futureTask);
        thread.start();
        try {
            System.out.println(futureTask.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

線程的生命週期

關於Java中線程的生命週期,首先看一下下面這張圖:

上圖中基本上囊括了Java中多線程各重要知識點。掌握了上圖中的各知識點,Java中的多線程也就基本上掌握了。

線程的基本狀態

新建狀態(New):當線程對象對建立後,即進入了新建狀態,如:Thread t = new MyThread();

就緒狀態(Runnable):當調用方法t.start()時,線程即進入就緒狀態。處於就緒狀態的線程,只是說明此線程已經作好了準備,隨時等待CPU調度執行,並非說執行了t.start()此線程當即就會執行;一樣還有幾種狀況會進行就緒狀態,請參見上圖。

運行狀態(Running):當CPU開始調度處於就緒狀態的線程時,此時線程才得以真正執行,即進入到運行狀態。注:就緒狀態是進入到運行狀態的惟一入口,也就是說,線程要想進入運行狀態執行,首先必須處於就緒狀態中;

阻塞狀態(Blocked):處於運行狀態中的線程因爲某種緣由,暫時放棄對CPU的使用權,中止執行,此時進入阻塞狀態,直到其進入到就緒狀態,才 有機會再次被CPU調用以進入到運行狀態。根據阻塞產生的緣由不一樣,阻塞狀態又能夠分爲三種:請參見上圖。

死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期。

線程的基本操做

sleep:使當前線程休眠指定毫秒

yield:使當前線程讓出CPU,從運行狀態轉到可運行狀態。注意僅僅是讓出,讓出以後也會加入到搶佔資源的隊伍中。

join:把指定的線程加入到當前線程,好比在線程B中調用了線程A的Join()方法,直到線程A執行完畢後,纔會繼續執行線程B

線程中止:

Thread自己提供了一個stop方法,可是這個不推薦使用。由於使用stop的時候會暴力終止線程,從而形成數據不一致。

優雅的中止線程的代碼舉例以下:

public class StopThread {
    static class ThreadDemo extends Thread {
        volatile boolean stopMe = false;
        public void stopMe(){
            this.stopMe=true;
        }
        @Override
        public void run() {
            while (true) {
                if (stopMe) {
                    System.out.println("程序結束");
                    break;
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo();
        threadDemo.start();
        threadDemo.stopMe();
    }
}

以一個volatile修飾的變量stopMe來控制線程的中止。

線程中斷:

線程中斷的相關方法分別是這三個

public void interrupt() ; //中斷線程
public boolean isInterrupted(); //判斷線程是否被中斷
public static boolean interrupted(); //判斷線程是否被中斷,並清除當前中斷狀態

線程中斷的代碼舉例以下:

public class InterruptThread {
    static class ThreadDemo extends Thread {
        @Override
        public void run() {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("程序結束");
                    break;
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo();
        threadDemo.start();
        threadDemo.interrupt();
    }
}

wait 和notify

這兩個方法是JDK爲了支持多線程之間的協做而提供的。

當在線程A中調用了obj.wait()方法時,線程A會中止執行進入等待狀態。直到其餘線程調用obj.notify()時纔會進入阻塞狀態繼而等待獲取鎖。

請看下方示例代碼

package cn.shiyujun.thread.hellothread;

/**
 * d
 *
 * @author syj
 * CreateTime 2019/03/19
 * describe:WaitNotify測試demo
 */
public class WaitNotifyThread {
    public static Object obj = new Object();

    static class WaitThreadDemo extends Thread {
        @Override
        public void run() {
            synchronized (obj) {
                try {
                    System.out.println("WaitThread wait,time=" + System.currentTimeMillis());
                    obj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("WaitThread end,time=" + System.currentTimeMillis());
            }
        }
    }

    static class NotifyThreadDemo extends Thread {
        @Override
        public void run() {
            synchronized (obj) {
                System.out.println("NotifyThread notify,time=" + System.currentTimeMillis());
                obj.notify();
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("NotifyThread end,time=" + System.currentTimeMillis());
            }
        }
    }

    public static void main(String[] args) {
        WaitThreadDemo waitThreadDemo = new WaitThreadDemo();
        NotifyThreadDemo notifyThreadDemo = new NotifyThreadDemo();
        waitThreadDemo.start();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        notifyThreadDemo.start();
    }
}

在上方的代碼中,Wait線程會首先獲取到obj的鎖,當它執行wait方法時就會釋放obj的鎖並進入等待狀態。這個時候Notify線程能夠獲取到obj的鎖,而且喚醒Wait線程,可是由於此時Notify線程是睡眠了2秒鐘以後才釋放的obj的鎖,因此Wait線程獲取鎖的時候Notify線程已經執行完畢了。

此程序的運行結果:

WaitThread wait,time=1553088237753
NotifyThread notify,time=1553088237862
NotifyThread end,time=1553088239867
WaitThread end,time=1553088239867

suspen和resume

它們兩個的功能是掛起線程和繼續執行,被suspen掛起的線程必須被resume喚醒才能夠繼續執行。乍看起來 能夠實現wait和notify的功能,不過我可不推薦你使用它們,和wait以後會釋放鎖不一樣,suspen掛起以後依然會持有鎖,這個可就很是危險了。

線程組

在一個系統中若是線程數量衆多而又功能比較一致,就能夠把這些線程放到一個線程組裏。

線程組示例代碼:

public class ThreadGroupDemo {
    static class ThreadDemo extends Thread {
        @Override
        public void run() {
            while (true){
                System.out.println("I am "+Thread.currentThread().getThreadGroup().getName()+"-"+Thread.currentThread().getName());
                try {
                    sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) {
        ThreadGroup threadGroup=new ThreadGroup("groupDemo");
        Thread t1=new Thread(threadGroup,new ThreadDemo(),"t1");
        Thread t2=new Thread(threadGroup,new ThreadDemo(),"t2");
        t1.start();
        t2.start();
    }
}

守護線程

在線程的世界裏,由咱們本身建立的線程叫用戶線程。而一些系統建立的線程,如垃圾回收線程等被稱之爲守護線程,若是想要把一個用戶線程設置爲守護線程能夠在線程的調用線程的start方法前設置線程的daemon屬性爲true;

t1.setDaemon(true);

當一個程序中只有守護線程的時候這個程序也就要結束了。

線程的優先級

Java中的線程能夠有本身的優先級,優先級高的線程在進行資源搶佔的時候每每會更有優點。線程飢餓現象就是因爲線程的優先級低沒法搶佔資源而引發的。

在Java中線程的優先級能夠設置的範圍是1到10之間,數字越大優先級越高。Java線程建立默認的優先級是5,咱們能夠線程start以前經過以下方式設置線程的優先級。

t1.setPriority(1);

針對於上面所涉及到的知識點我總結出了有1到5年開發經驗的程序員在面試中涉及到的絕大部分架構面試題及答案作成了文檔和架構視頻資料免費分享給你們(包括Dubbo、Redis、Netty、zookeeper、Spring cloud、分佈式、高併發等架構技術資料),但願能幫助到您面試前的複習且找到一個好的工做,也節省你們在網上搜索資料的時間來學習,也能夠關注我一下之後會有更多幹貨分享。

資料獲取方式: QQ羣搜索「708-701-457」 便可免費領取



相關文章
相關標籤/搜索