Java多線程(複習-1)線程基礎

一,進程,線程的概念:來自百度百科

進程(Process)是計算機中的程序關於某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位。

線程(英語:thread)是操作系統能夠進行運算調度的最小單位。

二,Java中線程創建線程的幾種方法

1,extends Thread :這種的侷限性是不支持多繼承,這也是受Java語言本身限制。其實Tread類是實現了Runnable<>

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

2,implements Runnable<> 。

3,implements Callable<> 。相比於前面創建的線程中,前者是實現 run() 這個是沒有返回值的,而call()方法是有返回值的,而且可以聲明異常,功能更加強大,這種實現是Java 爲高併發提供的一種Future模式的一種封裝。

三,run()  ,start()方法的區別:

若調用直接調用run(),就像調用普方法一樣。如果有多個任務,那麼第二個任務必須等到第一個任務執行完成後纔會執行第二個,這時整個系統調用就是同步的。

調用start()方法 ,系統會吧線程交給"線程規劃器" ,讓系統自行調度線程,這時線程的調用就會有隨機性, 如果第一個任務在等待,那麼第二個線程也會被調用,這時線程的調度就是異步 的,

四,線程安全,當多個線程訪問同一個資源,如果不做同步處理,那麼使的某個線程讀取的資源不是我們所期望的值。爲什麼會出現這種情況呢

1,先了解一下Java的內存模型 JMM

這是線程,主內存,工作內存的關係:

1,Java內存模型規定,所有變量會存貯在主內存中, 每個線程都會有自己的工作內存,私有,不共享,工作內存保存的變量都是主內存的變量副本拷貝,線程對變量的讀取只能在工作內存,線程無法直接操作主內存的數據,重點 : 線程之間變量值的傳遞需要通過主內存來完成,怎麼理解,舉個例子:假如一個線程要讀取一個變量,首先主內存會讀取這個變量值到工作內存中(這裏涉及read操作),然後工作內存吧這個讀取的值載入工作內存對應這個變量副本的的變量上(這是load操作,之前這個變量必須是先進行read操作的),這時執行引擎(可以簡單的認爲是Java vm stack上當前棧幀上的操作數棧,對數據的讀寫就是出入棧操作)就會把這個值壓入當前操作數棧的棧頂

ps:具體分析可以看看《深入理解Java虛擬機》

那麼當多個線程訪問同一個變量時,因爲工作內存和主內存之間存在copy ,那就需要時間,從而導致某個線程讀取的值並不是主內存中最新的值,或者是舊的值。這時就是導致非線程安全的問題。

2,與Java編程中的有所不同,這裏的變量主要包括 實例字段,靜態字段,構成數組組成的元素,不包括局部變量,他是線程私有的,不會被共享,也就不會存在競爭。

解決辦法就是 加鎖。

五,加鎖,synchronized 的使用 被加鎖的代碼塊被稱爲互斥區,臨界區,每個線程會爭搶去獲得這個互斥區的鎖,強不到鎖的線程就會等待。直到拿到這把鎖

1,在方法上加鎖  

public class Person{
    public String name;
    public synchronized void sayHello(String name){
        Sysyem.out.println(name+"say hello");
    }
}

2,使用同步代碼塊

public class Person{
    public String name;
    public void sayHello(String name){
        synchronied(this){
            Sysyem.out.println(name+"say hello");
        }
    }
}

效果一樣,有時第二種會更好。這裏只是簡單介紹。

六,如何正確停止一個線程。

 1,使用interrupt(),而不使用stop()該方法已經廢除。但前者調用後並不會立即停止線程

這個方法是判斷當前線程是否已經中斷。但會清楚中斷標記

這個方法是判斷線程是否已經中斷,不清楚中斷標記

這是一個本地方法 參數 ClearInterrupted:是否清除中斷標誌

中斷方法

能力有限o'o,知道前兩個方法的區別就行

2,異常法中斷線程

簡單的例子

public class MyThread implements Runnable{
    
    @Override
    public void run(){
        try{
            if(this.interruptor()){
                throws new Execption("中斷線程");
            }
        }catch(Exception e){
            e.printStackTrace();
        }    
    }
}    

3,在沉睡中中斷線程

public class SleepInterruptDemo extends Thread {

    public void run() {
        super.run();
        try {

            for (int i = 0; i < 500000; i++) {
                System.out.println("i=" + (i + 1));
            }
            System.out.println("run begin");
            Thread.sleep(2000);
            System.out.println("run end");
        } catch (Exception e) {
            System.out.println("進入run方法中的catch了!!");
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        SleepInterruptDemo thread = new SleepInterruptDemo();
        thread.start();
        thread.interrupt();
        System.out.println("main end!");
    }
}

小細節:若果在中斷前再次沉睡,這時調用中斷方法 會清除中斷標記,即再thread.interrupt()前面加一個Thread.sleep(100)。

建議使用異常法

七,幾個常用的方法:

1,yelid()   使當前線程放棄cpu的資源,將它讓給其他線程去執行。

2,Thread.currentThread.getId()   獲取當前線程的id標識

3,isAlive() 判斷當前方法是否存活

4,setPriority(int num) 設置線程的優先級 Java規定0~10 超出報錯,等級越高越有可能搶到cpu的資源

             1)具有繼承性 a 線程啓動b線程,那麼b線程擁有和a一樣的優先級。

              2)規則性,優先級高的線程大部分會先於低的執行玩,

              3)隨機性,優先級高的線程不一定都先執行完

 

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