五、Java高級特性(多線程基礎使用篇)

一、使用多線程有什麼優點?

使用多線程可以最大限度的利用cpu的空閒時間來處理其他任務,比如一邊讓操作系統處理正在打印的數據,一邊使用Word編輯文檔。CPU在這些任務之間不停的切換,由於切換的速度非常快,給使用者的感受就是這些任務幾乎是同時執行的。



以上圖1-3我們發現在單任務也就是單線程的情況下,任務2必須要等待任務1執行完才能執行。而在1-4中我們使用多線程的情況下,任務2不需要等待任務1執行完畢,CPU可以在任務1和2之間來回的切換。

二、Java中使用多線程

在Java的JDK開發包中已經自帶了對多線程技術的支持。實現多線程編程的方式主要有兩種,一種是繼承Thread,一種是實現Runnable接口。

1、繼承Thread

package com.company;

public class Main {
    public static void main(String[] args) {
        InnerThread t = new InnerThread();
        t.start();
    }
}

class InnerThread extends Thread {
    @Override
    public void run() {
        super.run();
        System.out.println("使用繼承Thread啓動了一個線程");
    }
}

使用繼承Thread啓動了一個線程

我們創建了一個InnerThread 類,繼承了Thread,並重寫了run方法。在main函數中我們創建InnerThread 對象,並調用了start方法,這樣就啓動了一個線程。線程的工作就是在run方法中。

2、實現Runnable接口

package com.company;

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

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("使用繼實現Runnable啓動了一個線程");
    }
}
使用繼實現Runnable啓動了一個線程

以上我們使用實現Runnable接口也啓動了一個線程。線程具體的工作在Runnable接口的run方法中。我們可以看到Thread類其實也實現了Runnable接口

public
class Thread implements Runnable {
    /* Make sure registerNatives is the first thing <clinit> does. */
    private static native void registerNatives();
    static {
        registerNatives();
    }

我們也可以通過傳入Thread對象的方式啓動一個線程,其實和實現Runnable是一樣的。

package com.company;

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

class MyThread extends Thread {
    @Override
    public void run() {
        super.run();
        System.out.println("通過傳入Thread對象的方式啓動一個線程");
    }
}

通過傳入Thread對象的方式啓動一個線程

三、Thread的一些常用Api

1、Thread.currentThread()

currentThread方法,指的是獲取當前代碼塊運行所在的線程。

package com.company;

public class Main {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable());
        t.start();
        System.out.println("1、當前線程:"+Thread.currentThread().getName());

    }
}
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("2、當前線程:"+Thread.currentThread().getName());
    }
}
1、當前線程:main
2、當前線程:Thread-0

我們可以看到main方法運行所在的線程的名字爲main,而MyRunnable中的run方法運行所在的線程名字爲Thread-0

2、isAlive()方法

判斷當前線程是否處於活動的狀態。活動狀態指的是線程已經啓動尚未終止,即線程處於正在運行或者準備開始運行的狀態。

public class Main {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable());
        t.start();
        System.out.println("1當前線程狀態:"+t.isAlive());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("2當前線程狀態:"+t.isAlive());
    }
}
class MyRunnable implements Runnable {
    @Override
    public void run() {

    }
}
1當前線程狀態:true
2當前線程狀態:false

在代碼中我們可以理解成調用start方法後線程變爲活動的狀態。以上我們看到調用start方法只會,由於我們啓動的線程沒有馬上執行完畢,我們打印了isAlive結果是true,當我們在main方法中休眠了1000毫秒,之後再去打印isAlive,我們啓動的線程已經執行完畢,所以結果是false。在上面的例子我們我們其實有兩個線程,一個是main方法所在的main線程,一個是我們啓動的線程,我們可以叫他做A線程。我們在main函數所在的線程中啓動了一個A線程,這兩個線程是異步執行任務,不需要排隊執行。啓動A線程之後,我們休眠了1000毫秒,我們休眠的線程是Main線程,對A線程沒有影響,當我們再去打印A線程的isAlive方法的時候,結果是false,因爲過了1000毫秒,A線程已經執行完畢,處於非活動狀態。

3、sleep

讓當前線程休眠(暫停執行)一段時間。

package com.company;

public class Main {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable());
        t.start();
        System.out.println("我們在"+Thread.currentThread().getName()+"休眠了1000毫秒");

    }
}
class MyRunnable implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("我們在"+Thread.currentThread().getName()+"休眠了1000毫秒");
    }
}
我們在main休眠了1000毫秒
我們在Thread-0休眠了1000毫秒

4、interrupt方法

使用interrupt方法來終止線程。

package com.company;

public class Main {
    public static void main(String[] args) {
        MyThread t = new MyThread();
        t.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t.interrupt();
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 500000; i++) {
            if (isInterrupted()) {
                break;
            }
            System.out.println("我正在執行..." + i);
        }
        System.out.println("執行完畢");

    }
}




我正在執行...
我正在執行...
我正在執行...
我正在執行...
我正在執行...
我正在執行...
我正在執行...
我正在執行...
true
執行完畢

在上面的例子中,我們在線程的run方法中,寫了一個for循環,通過isInterrupted的返回值來判斷是否跳出循環,結束循環裏的工作。在Main方法中,我們啓動了一個線程,休眠1000毫秒之後,我們調用interrupt方法,中斷了線程。所以看到以上的打印結果是執行了一段時間for循環裏的代碼之後,跳出循環並打印了執行完畢。以上的方式我們可以結束掉for循環裏的工作。但是線程沒有真正被執行完畢,因爲後面還是打印了執行完畢的字樣,說明線程還是在工作。那我們怎麼樣才能真正的停止線程呢?

package com.company;

public class Main {
    public static void main(String[] args) {
        MyThread t = new MyThread();
        t.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t.interrupt();
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 500000; i++) {
                if (isInterrupted()) {
                    throw new InterruptedException();
                }
                System.out.println("我正在執行..." + i);
            }
            System.out.println("執行完畢");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }
}

我正在執行...177357
我正在執行...177358
我正在執行...177359
我正在執行...177360
我正在執行...177361
我正在執行...177362
我正在執行...177363
我正在執行...177364
我正在執行...177365
我正在執行...177366
我正在執行...177367
我正在執行...177368
java.lang.InterruptedException
    at com.company.MyThread.run(Main.java:22)

我們通過拋出InterruptedException異常的方式來停止線程,我沒看到執行完畢這幾個字樣沒有執行了。

5、yield

yield方法指的是放棄當前CPU資源,給其他線程去佔用CPU資源。但是放棄時間不確定,有可能剛放棄又馬上獲得CPU時間片。因爲CPU調度線程是隨機的。

6、線程的優先級

通過setPriority方法可以設置線程的優先級,我們知道CPU調度線程是隨機的,設置線程的優先級越高,可以使得線程獲得CPU的資源越多。也就是CPU優先執行優先級高的線程。

優先級具有繼承性

A線程啓動B線程,那麼A線程和B線程的優先級一樣

優先級具有規則性和隨機性

優先級具有規則性指的是:CPU會盡量將執行資源讓給優先級比較高的,但是不是完全的。
優先級具有隨機性指的是:優先級高的線程不代表任務一定能執行完,也有可能優先級低的任務先執行完了。但是大概率會是優先級高的會優先執行完。

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