溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

java多線程的基礎知識有哪些

發(fā)布時間:2020-10-27 11:20:11 來源:億速云 閱讀:113 作者:小新 欄目:編程語言

小編給大家分享一下java多線程的基礎知識有哪些,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

Java 主線程名

我們啟動的一個程序可以理解為一個進程, 一個進程中包含一個主線程, 線程可以理解為一個子任務. Java 中可以通過下面代碼來獲取默認的主線程名.

System.out.println(Thread.currentThread().getName());

運行結果為 main, 這是線程的名字并不是 main 方法, 通過此線程來執(zhí)行 main 方法而已.

兩種方式創(chuàng)建線程

1.繼承 Thread 類

public class Thread1 extends Thread {
    @Override
    public void run() {
        System.out.println("qwe");
    }
}

2.實現(xiàn) Runnable 接口

public class Thread2 implements Runnable {
    @Override
    public void run() {
        System.out.println("asd");
    }
}
Thread 實現(xiàn) Runnable 接口. 并且多線程運行時, 代碼的執(zhí)行順序與調用順序是無關的. 另外如果多次調用 start方法則會拋出 java.lang.IllegalThreadStateException

currentThread 方法

返回當前代碼正在被哪個線程調用.

public class Thread1 extends Thread {

    public Thread1() {
        System.out.println("構造方法的打?。?quot; + Thread.currentThread().getName());
    }

    @Override
    public void run() {
        System.out.println("run 方法的打印:" + Thread.currentThread().getName());
    }
}
Thread1 thread1 = new Thread1();
thread1.start();

isAlive 方法

判斷當前線程是否處于活動狀態(tài).

public class Thread1 extends Thread {
    @Override
    public void run() {
        System.out.println("run 方法的打印 Thread.currentThread().isAlive() == " + Thread.currentThread().isAlive());
        System.out.println("run 方法的打印 this.isAlive() == " + this.isAlive());
        System.out.println("run 方法的打印 Thread.currentThread().isAlive() == this.isAlive() == " + (Thread.currentThread() == this ? "true" : "false"));
    }
}
Thread1 thread1 = new Thread1();

System.out.println("begin == " + thread1.isAlive());
thread1.start();
Thread.sleep(1000);
System.out.println("end == " + thread1.isAlive());

執(zhí)行結果如下

begin == false
run 方法的打印 Thread.currentThread().isAlive() == true
run 方法的打印 this.isAlive() == true
run 方法的打印 Thread.currentThread() == this == true
end == false

thread1 在 1秒之后執(zhí)行完成. 所以輸出結果為 false. 并且 Thread.currentThread() 和 this 是同一個對象, 可以理解成執(zhí)行當前 run 方法的線程對象就是我們自己(this).

如果將線程對象以構造參數(shù)的方式傳遞給 Thread 對象進行 start() 啟動時, 運行結果和前面實例是有差異的. 造成這樣的差異的原因還是來自于 Thread.currentThread() 和 this 的差異.

System.out.println("begin == " + thread1.isAlive());
//thread1.start();
// 如果將線程對象以構造參數(shù)的方式傳遞給 Thread 對象進行 start() 啟動
Thread thread = new Thread(thread1);
thread.start();

Thread.sleep(1000);
System.out.println("end == " + thread1.isAlive());

執(zhí)行結果

begin == false
run 方法的打印 Thread.currentThread().isAlive() == true
run 方法的打印 this.isAlive() == false
run 方法的打印 Thread.currentThread() == this == false
end == false

Thread.currentThread().isAlive() 是 true 的原因是因為, Thread.currentThread() 返回的是 thread 對象, 而我們也是通過此對象來啟動線程的, 所以是在活動狀態(tài).

this.isAlive() 是 false 就比較好理解了, 因為我們是通過 thread 對象啟動的線程來執(zhí)行 run 方法的. 所以它是 false. 同時也說明這兩個不是同一個對象.

sleep 方法

在指定的毫秒數(shù)內讓當前 "正在執(zhí)行的線程" 休眠. "正在執(zhí)行的線程" 只得是 Thread.currentThread() 返回的線程.

Thread.sleep(1000);

停止線程

停止一個線程意味著在線程處理完任務之前停掉正在做的操作, 也就是放棄當前的操作.

在 Java 中有以下 3 種方法可以終止正在運行的線程:

  1. 使用退出標志, 使線程正常退出, 也就是當 run 方法完成后線程終止.

  2. 使用 stop 方法強行終止線程, 但是不推薦使用這個方法.

  3. 使用 interrupt 方法中斷線程.

停不了的線程

調用 interrupt 方法僅僅是當前線程打了一個停止標記, 并不是真正的停止線程.

public class Thread1 extends Thread {
    @Override
    public void run() {

        for(int i = 0; i < 500000; i++) {
            System.out.println(i);
        }
    }
}
Thread1 thread1 = new Thread1();
thread1.start();
Thread.sleep(2000);
thread1.interrupt();

我們兩秒后調用 interrupt 方法, 根據(jù)打印結果我們可以看到線程并沒有停止, 而是在執(zhí)行完 500000 此循環(huán)后 run 方法結束線程停止.

判斷線程是否是停止狀態(tài)

我們將線程標記為停止后, 需要在線程內部判斷一下這個線程是否是停止標記, 如果是則停止線程.

兩種判斷方法:

  1. Thread.interrupted(); 也可以使用 this.interrupted();

  2. this.isInterrupted();

下面是兩個方法的源碼:

    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }
    
    public boolean isInterrupted() {
        return isInterrupted(false);
    }

interrupted() 方法數(shù)據(jù)靜態(tài)方法, 也就是判斷當前線程是否已經(jīng)中斷. isInterrupted() 判斷線程是否已經(jīng)被中斷.

來自官網(wǎng)的 interrupted() 方法重點.
線程的 中斷狀態(tài) 由該方法清除. 換句話說, 如果連續(xù)兩次調用該方法, 則第二次調用將返回 false (在第一次調用已清除了其中斷狀態(tài)之后, 且第二次調用檢驗完中斷狀態(tài)前, 當前線程再次中斷的情況除外).

異常停止線程

public class Thread1 extends Thread {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 500000; i++) {
                if (this.isInterrupted()) {
                    System.out.println("線程停止");
                    throw new InterruptedException();
                }

                System.out.println("i = " + i);
            }
        } catch (InterruptedException e) {
            System.out.println("線程通過 catch 停止");
            e.printStackTrace();
        }
    }
}
Thread1 thread1 = new Thread1();
thread1.start();
Thread.sleep(1000);
thread1.interrupt();

輸出結果, 這是最后幾行:

i = 195173
i = 195174
i = 195175
線程停止
線程通過 catch 停止
java.lang.InterruptedException
    at Thread1.run(Thread1.java:9)
當然我們也可以將 throw new InterruptedException(); 換成 return. 都是一樣可以結束線程的.

在沉睡中停止

如果線程調用 Thread.sleep() 方法使線程進行休眠, 這時我們調用 thread1.interrupt()后會拋出. InterruptedException 異常.

java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at Thread1.run(Thread1.java:8)

暴力停止線程

暴力停止線程可以使用 stop 方法, 但此方法已經(jīng)過時并不推薦使用, 原因如下.

  1. 即刻拋出 ThreadDeath 異常, 在線程的run()方法內, 任何一點都有可能拋出ThreadDeath Error, 包括在 catch 或 finally 語句中. 也就是說代碼不確定執(zhí)行到哪一步就會拋出異常.

  2. 釋放該線程所持有的所有的鎖. 這可能會導致數(shù)據(jù)不一致性.

public class Thread1 extends Thread {

    private String userName = "a";
    private String pwd = "aa";

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public void run() {
        this.userName = "b";
        try {
            Thread.sleep(100000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.pwd = "bb";

    }
}
Thread1 thread1 = new Thread1();
thread1.start();
Thread.sleep(1000);
thread1.stop();

System.out.println(thread1.getUserName() + "     " + thread1.getPwd());

輸出結果為:

b     aa

我們在代碼中然線程休眠 Thread.sleep(100000); 是為了模擬一些其它業(yè)務邏輯處理所用的時間, 在線程處理其它業(yè)務的時候, 我們調用 stop 方法來停止線程.

線程是被停止了也執(zhí)行了 System.out.println(thread1.getUserName() + "     " + thread1.getPwd()); 來幫我們輸出結果, 但是 this.pwd = "bb"; 并沒有被執(zhí)行.

所以, 當調用了 stop 方法后, 線程無論執(zhí)行到哪段代碼, 線程就會立即退出, 并且會拋出 ThreadDeath 異常, 而且會釋放所有鎖, 從而導致數(shù)據(jù)不一致的情況.

interrupt 相比 stop 方法更可控, 而且可以保持數(shù)據(jù)一致, 當你的代碼邏輯執(zhí)行完一次, 下一次執(zhí)行的時候, 才會去判斷并退出線程.

如果大家不怎么理解推薦查看 為什么不能使用Thread.stop()方法? 這篇文章. 下面是另一個比較好的例子.

如果線程當前正持有鎖(此線程可以執(zhí)行代碼), stop之后則會釋放該鎖. 由于此錯誤可能出現(xiàn)在很多地方, 那么這就讓編程人員防不勝防, 極易造成對象狀態(tài)的不一致. 例如, 對象 obj 中存放著一個范圍值: 最小值low, 最大值high, 且low不得大于high, 這種關系由鎖lock保護, 以避免并發(fā)時產生競態(tài)條件而導致該關系失效.

假設當前l(fā)ow值是5, high值是10, 當線程t獲取lock后, 將low值更新為了15, 此時被stop了, 真是糟糕, 如果沒有捕獲住stop導致的Error, low的值就為15, high還是10, 這導致它們之間的小于關系得不到保證, 也就是對象狀態(tài)被破壞了!

如果在給low賦值的時候catch住stop導致的Error則可能使后面high變量的賦值繼續(xù), 但是誰也不知道Error會在哪條語句拋出, 如果對象狀態(tài)之間的關系更復雜呢?這種方式幾乎是無法維護的, 太復雜了!如果是中斷操作, 它決計不會在執(zhí)行l(wèi)ow賦值的時候拋出錯誤, 這樣程序對于對象狀態(tài)一致性就是可控的.

suspend 與 resume 方法

用來暫停和恢復線程.

獨占

public class PrintObject {
    synchronized public void printString(){
        System.out.println("begin");
        if(Thread.currentThread().getName().equals("a")){
            System.out.println("線程 a 被中斷");
            Thread.currentThread().suspend();
        }
        if(Thread.currentThread().getName().equals("b")){
            System.out.println("線程 b 運行");
        }
        System.out.println("end");
    }
}
try{
    PrintObject  pb = new PrintObject();
    Thread thread1 = new Thread(pb::printString);
    thread1.setName("a");
    thread1.start();
    thread1.sleep(1000);

    Thread thread2 = new Thread(pb::printString);
    thread2.setName("b");
    thread2.start();
}catch(InterruptedException e){ }

輸出結果:

begin
線程 a 被中斷

當調用 Thread.currentThread().suspend(); 方法來暫停線程時, 鎖并不會被釋放, 所以造成了同步對象的獨占.

以上是java多線程的基礎知識有哪些的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業(yè)資訊頻道!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。

AI