2、線程的基本使用

一、創建線程的兩種方式

在Thread類中,官方給出的創建線程有兩種方式:

  1. 繼承Thread類並實現run()方法;

  2. 實現Runnable接口,並實現run()方法。
    在這裏插入圖片描述

    1. 當然我們也可以通過實現Callable接口,實現call()方法創建一個線程。

/**
 * 創建線程的兩種方式:
 *      1、繼承Thread
 *      2、實現Runnable
 *      3、實現Callable
 */
public class CreateThreadMain {
     public static void main(String[] args) throws ExecutionException, InterruptedException {
        //創建一個通過繼承Thread類,創建一個線程
        Thread extendsThreadWay = new ExtendsThreadWay();
        //實現Runnable 創建一個線程
        Thread implementRunnableWay =new Thread(new ImplementRunnableWay());
        //實現Callable  創建一個線程,futureTask.get()獲取線程返回的結果
        FutureTask<String> futureTask = new FutureTask<>(new ImplementCallableWay());
        Thread implementCallableWay = new Thread(futureTask);
        //啓動線程
        extendsThreadWay.start();
        implementCallableWay.start();
        implementRunnableWay.start();
        TimeUnit.SECONDS.sleep(1);
        System.out.println("通過Callable獲取的結果:futureTask.get() = " + futureTask.get());
    }
}

/**
 * 通過繼承Thread實現線程
 */
 class ExtendsThreadWay extends Thread {
    @Override
    public void run() {
        System.out.println("這是通過實現Thread來創建的線程。。。start");
    }
}

/**
 * 通過實現Callable創建線程
 */
class ImplementCallableWay implements Callable<String> {
    @Override
    public String call() throws Exception {
        System.out.println("這是通過實現Callable來創建的線程。。。start\"");
        return "result";
    }
}

/**
 * 通過實現runnable創建線程
 */
class ImplementRunnableWay implements Runnable{
    @Override
    public void run() {
        System.out.println("這是通過實現Runnable來創建的線程。。。start");
    }
}

運行結果如下:
在這裏插入圖片描述

二、線程的啓動

創建完一個線程可以通過start()方法啓動線程,並不能通過調用run()方法去啓動線程。因爲直接調用run()方法只是主線程中普通方法的調用,仍然使用主線程去執行的這個方法。


/**
 * 調用run()和start()的區別
 * 使用start()纔是真正啓動了線程
 */
public class ComparisonRunAndStart {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new RunAndStart();
        //將當前主線程的名稱設爲main  表示使用Run方法仍然在當前主線程運行,並未開啓新的線程
        Thread.currentThread().setName("main");
        //將新的線程名稱設爲new thread
        thread.setName("new thread");
        //使用run方法
        System.out.println("使用run啓動。。。。。仍然在當前線程");
        thread.run();
        Thread.sleep(1000);
        //使用start啓動
        System.out.println("使用start啓動。。。。。開啓了新的線程");
        thread.start();
    }
}
class RunAndStart extends Thread{
    @Override
    public void run() {
    		//打印出執行這個方法的線程名稱
            System.out.println("I am :"+Thread.currentThread().getName());
    }
}

運行結果如下:
線程啓動運行結果

三、線程的停止

  1. 線程的自然終止:當run()方法中的任務執行完畢或者線程拋出了異常,線程提前結束,那麼這個線程就會終止。
  2. 線程人爲終止:可通過stop()、suspend()、resume()方法終止、掛起和恢復線程。但是這些方法是過期的,不建議使用。因爲stop()方法會強行中斷線程,無法保證資源的正常釋放;suspend()方法使線程進入掛起狀態但是不會釋放資源,容易引發死鎖的問題;resume()與resume()方法配合使用,恢復掛起的線程。
  3. 線程的中斷:其他線程可以通過A線程interrupt()方法安全的中斷A線程,A線程通過檢查自身的中斷標誌位是否被置爲true來進行響應。A線程通過isInterrupted()方法或者Thread.interrupted()來判斷是否被中斷,不同的是Thread.interrupted()會將中斷表示爲置爲false。當一個線程處於阻塞狀態(sleep、join、wait等)時,如果檢查到中斷標誌位爲true會拋出InterruptedException異常,並將中斷標誌位置爲false。注意:處於死鎖狀態的線程無法被中斷。
    正常中斷線程示例:

/**
 * 使用interrupted中斷線程
 */
public class InterruptedThread implements Runnable {
    @Override
    public void run() {
    	//如果代碼沒被中斷,持續執行
        while (!Thread.currentThread().isInterrupted()){
            System.out.println("線程運行中。。");
        }
    }
}
class Main{
    public static void main(String[] args) throws InterruptedException {
        //創建一個線程
        Thread thread = new Thread(new InterruptedThread());
        long startMillis = System.currentTimeMillis();
        //啓動線程
        thread.start();
        Thread.sleep(1000);
        //1s後中斷線程
        thread.interrupt();
        System.out.println("線程被中斷,運行了:" + (System.currentTimeMillis() - startMillis) +"ms");
    }
}

運行結果:
在這裏插入圖片描述
處於阻塞狀態的線程被中斷:


/**
 1. 使用interrupted中斷阻塞線程
 */
public class InterruptedThread implements Runnable {
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()){
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("線程運行中。。");
        }
    }
}
class Main{
    public static void main(String[] args) throws InterruptedException {
        //創建一個線程
        Thread thread = new Thread(new InterruptedThread());
        long startMillis = System.currentTimeMillis();
        //啓動線程
        System.out.println("啓動線程。。。");
        thread.start();
        Thread.sleep(2);
        //1s後中斷線程
        thread.interrupt();
        System.out.println("線程被中斷,運行了:" + (System.currentTimeMillis() - startMillis) +"ms");
    }
}

運行結果,拋出InterruptedException異常,並將中斷標誌位置爲false,導致線程持續運行:
在這裏插入圖片描述

四、線程的其他方法

  1. yield()方法:使當前線程讓出CPU執行權,進入就緒狀態,重新競爭CPU執行權,因此有可能在調用yield()方法之後,立馬又開始執行這個線程。這個操作不會釋放鎖資源。
  2. join()方法,在A線程中調用B線程的join()方法,會把指定的B線程加入到當前A線程中,直到B線程執行完畢繼續執行A線程。他可以使兩個並行執行的線程變爲順序執行。

/**
 * 使用join()可以把指定線程加入到當前線程,使兩個線程順序運行。
 * 如:在A線程中調用B線程的join方法,則開始執行B線程,直到B線程執行完畢後,繼續執行A線程。
 */
public class UseJoinMain {
    public static void main(String[] args) {
        Thread threadB = new ThreadB();
        Thread threadA = new ThreadA(threadB);
        //啓動A線程
        threadA.start();
        //啓動B線程
        threadB.start();
    }
}

/**
 * 線程A
 */
class ThreadA extends Thread{
    private Thread threadB;

    public ThreadA(Thread threadB) {
        this.threadB = threadB;
    }

    @Override
    public void run() {
        System.out.println("線程A開始執行。。。。");
        try {
            System.out.println("線程B加入執行。。。。線程A進入阻塞狀態");
            threadB.join();
            System.out.println("線程A恢復執行。。。。");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("線程A執行完畢。。。。。");
    }
}
/**
 * 線程B
 */
class ThreadB extends Thread{
    @Override
    public void run() {
        System.out.println("線程B開始執行。。。");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("線程B執行完畢。。。");
    }
}

執行結果:
![在這裏插入圖片描述](https://img-blog.csdnimg.cn/20200311172110351.png)

  1. setPriority(int n):設置線程的優先級,參數範圍爲1~10,默認優先級是5。優先級高的線程分配時間片的數量要多於優先級低的線程。設置線程優先級時,針對頻繁阻塞(休眠或者I/O操作)的線程需要設置較高優先級,而偏重計算(需要較多CPU時間或者偏運算)的線程則設置較低的優先級,確保處理器不會被獨佔。在不同的JVM以及操作系統上,線程規劃會存在差異,有些操作系統甚至會忽略對線程優先級的設定。

/**
 * 優先級可設置爲1~10  優先級越高 被分配的時間片越長
 * 對於偏於運算,比較耗CPU的線程可設置低的優先級 確保CPU不被獨佔
 *
 *
 * 基本沒啥用 受操作系統與JVM影響,有的系統會忽略優先級這個設定
 */
public class PriorityThreadMain {
    public static void main(String[] args) {
        PriorityThread priorityThread = new PriorityThread();
        priorityThread.setPriority(10);
        priorityThread.start();
    }
}

class PriorityThread extends Thread{
    @Override
    public void run() {
        while (true){
            System.out.println("當前線程優先級"+currentThread().getPriority());
        }
    }
}

在這裏插入圖片描述
4. setDaemon(true):將線程設置爲守護線程,守護線程是一種支持型線程,他主要用作程序後臺調用和支持性工作,比如垃圾回收就是一個守護線程。守護線程隨着主線程的結束而退出,守護線程中的finally塊中的內容不一定會執行。


/**
 * 守護線程隨着主線程的結束而結束
 * 守護線程中的finally不一定會執行
 */
public class DaemonThreadMain {

    static {
        Thread thread = new DaemonThread();
        thread.setDaemon(true);
        thread.start();
    }

    public static void main(String[] args) throws InterruptedException {
        Thread.sleep(1000);
    }

}
class DaemonThread extends Thread{

    @Override
    public void run() {
        try {
            while (true){
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("daemon thread is running............");
            }
        } finally {
            System.out.println("finally .......................");
        }
    }
}

執行結果:
在這裏插入圖片描述

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