Java十分鐘入門多線程中篇

舉例說明:

我們知道飛機在天上飛行是有固定的航線(可以理解成線程),每個機場都有最大的運行負載能力,當運行情況超過瞭負載能力的時候,這就需要塔臺調度參與,會根據每架飛機的優先級排序。當在航線的時候,如果出現緊急情況,會讓其他飛機避讓,讓這架飛機優先級提高,先降落。這就是調度,計算機程序線程運行也是這樣的。

1、線程的調度:

在Java多線程中,主要可以通過下面四個方法來分配CPU的使用權:

  • 設置優先級(Priority) 設置線程的優先級,值是1-10
  • 休眠(sleep) 單位毫秒,讓本線程屬於阻塞狀態,CPU會執行其他線程
  • 強制運行(join) 讓這個線程強制獲取CPU資源來運行
  • 禮讓(yield) 暫停正在執行的線程,讓其他線程先執行,執行完瞭在接著執行

1、設置優先級(Priority):

有兩個線程,分別設置最大優先級和最小優先級:

public class MyThread implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+"正在運行:"+i);
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(new MyThread(),"線程A:");
        Thread t2 = new Thread(new MyThread(),"線程B***:");

        //設置優先級: 最高為10  最低為1
        t1.setPriority(Thread.MAX_PRIORITY);
        t2.setPriority(Thread.MIN_PRIORITY);

        //顯示線程優先級:
        System.out.println("線程A的優先級是:"+t1.getPriority());
        System.out.println("線程B的優先級是:"+t2.getPriority());

        t1.start();
        t2.start();
    }
}

結果:

2、休眠(sleep)

Thread.sleep();——–單位是毫秒,讓本線程屬於阻塞狀態,CPU會執行其他線程:

public class ThreadSleep {
    public static void main(String[] args) {
        sleepTime(5);
    }

    private static void sleepTime(int time) {
        for (int i = 0; i < 5; i++) {
            System.out.println("主線程執行瞭:"+i+"s");
            try{
                //讓線程休眠,進入阻塞狀態
                Thread.sleep(1000); //休眠時間為1000毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

結果:

3、強制運行(join)

顧名思義,就是讓某個線程強制進入執行:

子線程:

public class MyThread {
}

測試類:

public class Test {
    public static void main(String[] args) throws InterruptedException {

        Thread t1=  new Thread(new MyThread(),"我是子線程");
        t1.start();
        //當主線程執行任務1-10時,如果執行到5就讓子線程t1強制進來執行,直到執行完瞭才讓主線程繼續執行任務
        for (int i = 0; i < 6; i++) {
            if (i==2){
                t1.join();
            }
            System.out.println(Thread.currentThread().getName()+"正在運行:"+i);
        }
    }
}

結果:

4、禮讓(yield)

暫停正在執行的線程,讓其他線程先執行,執行完瞭在接著執行:

public class MyThread implements Runnable{

    //線程禮讓,讓本線線程阻塞,其他線程先執行
    //這裡就是A線程運行二次後,禮讓,讓B 線程先執行
    //也是理論上的,就是不管怎麼樣第二次後面肯定讓B先執行,但是後面就隨機瞭
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+"正在運行:"+i);
            if(i == 2){
                Thread.yield();
            }
        }
    }
}

測試類:

public class Test {
    public static void main(String[] args) {

        Thread t1 = new Thread(new MyThread(),"線程A:");
        Thread t2 = new Thread(new MyThread(),"----線程B");

        t1.start();
        t2.start();
    }
}

結果:

2、定時器線程:

定時器就是可以設置某個時間點來執行某個事件,比如系統每周刪除依次日志文件,或者在指定的日期關閉系統,定時器本身就是一個線程任務來在指定的時候執行該任務。定時器是繼承TimerTask來重寫run方法,專門處理定時任務。

演示Demo:

public class MyThread extends TimerTask {

    @Override
    public void run() {
        //把任務定義在run方法中
        showMyself();
    }

    public void showMyself(){
        System.out.println("被Run方法執行的");
    }
}

測試類:

public class Test {
    public static void main(String[] args) {

        Timer timer = new Timer();

        //設置5秒後執行這個任務,並且每1秒重復執行這個任務

        timer.schedule(new MyThread(),5000,1000);

    }
}

結果:

3、線程的同步:

首先我們先看一個demo:

創建瞭兩個線程對象,一個線程A任務用於執行print1,另一個線程B任務用於執行print2:

    public void print1(){
        System.out.print("中");
        System.out.println("國");
    }

    public void print2(){
        System.out.print("浙");
        System.out.println("江");
    }
}

測試類:

public class Test {
    public static void main(String[] args) {

        Printer p = new Printer();
        //A:
        new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    p.print1();
                }
            }
        },"線程A:").start();

        //B:
        new Thread("線程B:"){
            @Override
            public void run(){
                while (true){
                    p.print2();
                }
            }
        }.start();
    }
}

這個程序就是當線程A執行的時候,輸出中國,當B執行的時候,輸出浙江,理論上是沒有任何問題,但是我們看一下結果:

我們發現出問題瞭,其實這就是非線程同步(異步):

  • 同步:提交請求->等待服務器處理->處理完返回 這個期間客戶端瀏覽器不能幹任何事
  • 異步:請求通過事件觸發->服務器處理(這是瀏覽器仍然可以作其他事情)->處理完畢

其實非線程同步在有些系統是很危險的問題,比如12306,如果使用非線程同步,那麼後果可想而知,那麼該如何同步呢? 這裡介紹一個常用的方法,就是上鎖:

如果兩端代碼(兩個線程任務)是同步的,那麼CPU同一時間隻能執行一個任務,相當於給該線程上瞭一把鎖, 在該線程沒有執行完這段代碼或者任務的時候,其他線程是不能占用CPU資源執行任務的。直到執行完瞭該線 程的代碼,其他線程才可以執行。

更好的理解(舉例):

你去公共廁所上廁所(大的),當你進去後,需要把門關上並鎖著,這就是上鎖,為瞭保證你正常的結束(保證線程正常運行完),這期間其他人是不能進來的。

Synchronized 鎖:

兩個線程任務使用同一個對象為鎖,那麼兩個線程方法是同步的 我們把上面的方法上個鎖:

class Demo {
}

public class Printer {

   //創建任意一個對象,隻要是對象相同就是鎖相同,就是同步的!
    Demo d = new Demo();  

    public void print1(){
    	//當進入print1這個方法時,synchronized就給這個方法上瞭一個鎖
        synchronized (d){     
            System.out.print("中");
            System.out.println("國");
        }
    }

    public void print2(){
        //當進入print2這個方法時,synchronized也給這個方法上瞭一個鎖
        synchronized (d){
            System.out.print("浙");
            System.out.println("江");
        }
    }

}

這樣輸出後就不會出現上面的那個問題,這裡就不發結果截圖啦。大傢可以自己試一試,看看是否解決瞭這個問題~

我們還可以把鎖直接定義在方法上,比如這樣子:

    public static synchronized void print1(){
            System.out.print("中");
            System.out.println("國");
    }

如果是靜態方法,上鎖的方式是通過.class字節碼對象:

    public static void print1() {
        synchronized (Printer.class) {
            System.out.print("中");
            System.out.println("國");
        }
    }

小結: 這篇文章主要介紹瞭線程的調度(四種)、線程的定時以及線程的同步,內容也不少,多線程是Java的一個難點,也沒用一些華麗的圖片來演示,在學習過程中會比較枯燥,包括我自己也是。大傢也可以看看視頻,加深理解和印象!

到此這篇關於Java十分鐘入門多線程中篇的文章就介紹到這瞭,更多相關Java 多線程內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: