多線程常用操作

線程命名與取得

多線程的運行狀態是不確定的,所以對於多線程操作必須有一個明確標識出線程對象的信息,這個信息往往通過名稱來描述。在Thread類中提供有如下的線程名稱方法:
在這裏插入圖片描述

class MyThread implements Runnable {
    @Override
    public void run() {}
}
public class TestThread {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread thread = new Thread(myThread,"線程1");
        thread.start();
        System.out.println(thread.getName());

        Thread thread1 = new Thread(new MyThread());
        thread1.start();
        thread1.setName("線程2");
        System.out.println(thread1.getName());

        Thread thread2 = new Thread(new MyThread());
        thread2.setName("線程3");
        thread2.start();
        System.out.println(thread2.getName());
        
        thread2.setName("子線程3");
        System.out.println(thread2.getName());
    }
}

如何證明mian方法也是一個線程?

class MyThread implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
public class TestThread {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.run();
    }
}


Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.java.TestThread
main

Process finished with exit code 0

線程休眠(sleep方法)

線程休眠:指的是讓線程暫緩執行一下,等到了預計時間之後再恢復執行。

public static native void sleep(long millis) throws InterruptedException

線程休眠會交出CPU(變回阻塞狀態),讓CPU去執行其他的任務,睡眠結束後返回就緒狀態。但是有一點要非常注意,sleep方法不會釋放鎖,也就是說如果當前線程持有對某個對象的鎖,則即使調用sleep方法,其他線程也無法訪問這個對象。
休眠時間使用毫秒作爲單位

class MyThread implements Runnable {
    @Override
    public void run() {
        for(int i = 0;i < 100;i++){
            try {
                Thread.sleep(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(i);
        }
    }
}
public class TestThread {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        new Thread(myThread).start();
    }
}

線程讓步(yield()方法)

暫停當前正在執行的線程對象,並執行其他線程。
意思就是調用yield方法會讓當前線程交出CPU權限,讓CPU去執行其他的線程。它跟sleep方法類似,同樣不會釋放鎖。但是yield不能控制具體的交出CPU的時間,另外,yield方法只能讓擁有相同優先級的線程有獲取CPU執行時間的機會
注意,調用yield方法並不會讓線程進入阻塞狀態,而是讓線程重回就緒狀態,它只需要等待重新獲取CPU執行時間,這一點是和sleep方法不一樣的。

class MyThread implements Runnable {
    @Override
    public void run() {
        for(int i = 0;i < 100;i++){
            Thread.yield();
            System.out.println(Thread.currentThread().getName()+","+i);
        }
    }
}
public class TestThread {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        new Thread(myThread).start();
        new Thread(myThread).start();
        new Thread(myThread).start();
    }
}

join()方法

等待該線程終止。意思就是如果在主線程中調用該方法時就會讓主線程休眠,讓調用該方法的線程run方法先執行完畢之後在開始執行主線程。
join()方法只是對Object提供的wait()做的的一層包裝而已。
join()方法變回阻塞狀態結束後返回就緒狀態會釋放鎖


    class MyThread implements Runnable {
    @Override
    public void run() {
        try{
            System.out.println("主線程睡眠前的時間");
            TestThread.printTime();
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName());
            System.out.println("睡眠結束的時間");
            TestThread.printTime();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
public class TestThread {
    public static void main(String[] args) {
        Thread thread = new Thread(new MyThread(),"子線程");
        thread.start();
        System.out.println(Thread.currentThread().getName());
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("代碼結束");
    }
    public static void printTime() {
        Date date=new Date();
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String time = dateFormat.format(date);
        System.out.println(time);
    }
}


Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.java.TestThread
main
主線程睡眠前的時間
2018-11-14 13:07:10
子線程
睡眠結束的時間
2018-11-14 13:07:12
代碼結束

Process finished with exit code 0

5線程停止

多線程中有三種方式可以停止線程。

  • 設置標記位,可以是線程正常退出。
class MyThread implements Runnable {
    private boolean flag = true;
    @Override
    public void run() {
        int i = 0;
        while (flag) {
            System.out.println(Thread.currentThread().getName() + "第" + ++i + "次執行");
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}
public class TestThread {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread thread = new Thread(myThread,"子線程");
        thread.start();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        myThread.setFlag(false);
        System.out.println("代碼結束");
    }
}


Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.java.TestThread
子線程第1次執行
子線程第2次執行
子線程第3次執行
子線程第4次執行
子線程第5次執行
代碼結束

Process finished with exit code 0

  • 使用stop方法強制使線程退出,但是該方法不太安全所以已經被廢棄了

爲什麼說不安全呢?因爲stop會解除由線程獲取的所有鎖定,當在一個線程對象上調用stop()方法時,這個線程對象所運行的線程就會立即停止,假如一個線程正在執行:synchronized void { x = 3; y = 4;} 由於方法是同步的,多個線程訪問時總能保證x,y被同時賦值,而如果一個線程正在執行到x = 3;時,被調用了 stop()方法,即使在同步塊中, 它也會馬上stop了,這樣就產生了不完整的殘廢數據。

class MyThread implements Runnable {
    @Override
    public void run() {
        int i = 0;
        while (true) {
            System.out.println(Thread.currentThread().getName() + "第" + ++i + "次執行");
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
public class TestThread {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread thread = new Thread(myThread,"子線程");
        thread.start();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.stop();
        System.out.println("代碼結束");
    }
}
  • 使用Thread類中的一個interrupt() 可以中斷線程。

調用Thread類的interrupt()方法,只是將線程狀態置爲中斷狀態而已,他不會中斷一個正在運行的線程。此方法只是給受阻塞的線程傳遞一箇中斷信號,程序可以根據此信號來判斷是否需要終止。

具體而言,這個方法只會給線程設置一個爲true的中斷標誌(中斷標誌只是一個布爾類型的變量),而設置之後,則根據線程當前的狀態進行不同的後續操作。如果,線程的當前狀態處於非阻塞狀態,那麼僅僅是線程的中斷標誌被修改爲true而已;如果線程的當前狀態處於阻塞狀態,那麼在將中斷標誌設置爲true後,還會有如下三種情況之一的操作:
當線程中使用了sleep、wait、join()導致此線程阻塞,則interrupt()會在線程中拋出InterruptException,並且將線程的中斷狀態由true置爲false。
如果在中斷時,線程正處於非阻塞狀態,則將中斷標誌修改爲true,而在此基礎上,一旦進入阻塞狀態,則按照阻塞狀態的情況來進行處理。

通過上面的分析,我們可以總結,調用線程類的interrupted方法,其本質只是設置該線程的中斷標誌,將中斷標誌 設置爲true,並根據線程狀態決定是否拋出異常。因此,通過interrupted方法真正實現線程的中斷原理是:開發人員根據中斷標誌的具體值,來決定如何退出線程。

class MyThread implements Runnable {

    @Override
    public void run() {
        int i = 0;
        while (true) {
            try {
                /**
                 * 這裏阻塞之後,線程被調用了interrupte()方法,
                 * 清除中斷標誌,就會拋出一個異常
                 * java.lang.InterruptedException                    */
                Thread.sleep(1000);
                boolean bool = Thread.currentThread().isInterrupted();
                if(bool){
                    System.out.println("非阻塞狀態下執該操作...線程狀態:"+bool);
                    break;
                }
                System.out.println("第"+(++i)+"次執行,"+bool+",線程名稱爲:"+Thread.currentThread().getName());
            } catch (Exception e) {
                System.out.println("退出了");
                /**
                 * 這裏退出阻塞狀態,且中斷標誌被系統會自動清除
                 * 並且重新設置爲false,所以此處bool爲false
                 */
                boolean bool = Thread.currentThread().isInterrupted();
                System.out.println(bool);
                //退出run方法,中斷進程
                return;
            }
        }
    }
}
public class TestThread {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread thread = new Thread(myThread,"子線程");
        thread.start();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt();
        System.out.println("代碼結束");
    }
}


Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.java.TestThread
第1次執行,false線程名稱爲:子線程
第2次執行,false線程名稱爲:子線程
第3次執行,false線程名稱爲:子線程
第4次執行,false線程名稱爲:子線程
代碼結束
退出了
false

Process finished with exit code 0

線程優先級

線程的優先級指的是,線程的優先級越高越有可能先執行,但僅僅是有可能而已。

  • 設置優先級
public final void SetPriority(int newPriority)
  • 取得優先級
public final int getPriority()

對於優先級設置的內容可以通過Thread類的幾個常量來決定

  1. 高優先級:public final static int MAX_PRIORITY = 10;
  2. 中等優先級:public final static int NORM_PRIORITY = 5;
  3. 低優先級:public final static int MIN_PRIORITY = 1;
public class TestThread {
    public static void main(String[] args) {
        Runnable runnable = () -> System.out.println(Thread.currentThread().getName());
        Thread thread1 = new Thread(runnable,"子線程1");
        Thread thread2 = new Thread(runnable,"子線程2");
        Thread thread3 = new Thread(runnable,"子線程3");
        thread1.setPriority(Thread.MIN_PRIORITY);
        thread2.setPriority(Thread.MAX_PRIORITY);
        thread3.setPriority(Thread.NORM_PRIORITY);
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

主方法是一個線程,那麼主線程的優先級是什麼呢?

主方法只是一箇中等優先級,爲5

public class TestThread {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getPriority());   //5 
    }
}

線程具有繼承性

只是繼承優先級而已。從A線程啓動B線程,則B和A的線程優先級是一樣的

class A implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+","+Thread.currentThread().getPriority());
        new Thread(new B(),"線程B").start();
    }
}
class B implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+","+Thread.currentThread().getPriority());
    }
}
public class TestThread {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+","+Thread.currentThread().getPriority());
        Thread thread = new Thread(new A(),"線程A");
        thread.setPriority(Thread.MAX_PRIORITY);
        thread.start();
    }
}

Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.java.TestThread
main,5
線程A,10
線程B,10

守護線程

守護線程是一種特殊的線程,它屬於是一種陪伴線程。簡單點說 java 中有兩種線程:用戶線程和守護線程。可以通過isDaemon()方法來區別它們:如果返回false,則說明該線程是“用戶線程”;否則就是“守護線程”。典型的守護線程就是垃圾回收線程注意:主線程main是用戶線程
只要當前JVM進程中存在任何一個非守護線程沒有結束,守護線程就在工作;只有當後一 個非守護線程結束時,守護線程纔會隨着JVM一同停止工作。
Thread類提供setDaemon()將用戶線程設置爲守護線程

class MyThread implements Runnable {
    @Override
    public void run() {
        while(true){
            System.out.println(Thread.currentThread().getName()+"是否爲守護線程"+Thread.currentThread().isDaemon());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName() + "中斷線程了");
                return;
            }
        }
    }
}
public class TestThread {
    public static void main(String[] args) throws InterruptedException{
        Thread thread1 = new Thread(new MyThread(),"線程1");
        thread1.setDaemon(true);

        Thread thread2 = new Thread(new MyThread(),"線程2");
        thread1.start();
        thread2.start();

        Thread.sleep(3000);
        thread2.interrupt();
        Thread.sleep(5000);
        System.out.println("代碼結束");
    }
}

Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.java.TestThread
線程1是否爲守護線程true
線程2是否爲守護線程false
線程1是否爲守護線程true
線程2是否爲守護線程false
線程2是否爲守護線程false
線程1是否爲守護線程true
線程2中斷線程了
線程1是否爲守護線程true
線程1是否爲守護線程true
線程1是否爲守護線程true
線程1是否爲守護線程true
線程1是否爲守護線程true
代碼結束

Process finished with exit code 0

從上面的代碼可以看出來,B是用戶線程當它中斷了之後守護線程還沒有結束,是因爲主線程(用戶線程)還沒有結束,所以說明是所有的用戶線程結束之後守護線程纔會結束。

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