線程題目(實現線程的交替運行)以及類似題目

我是小康小白,一個平平無奇的Java小白。熱愛有趣的文字,生活和遠方。
個人博客:https://blog.csdn.net/weixin_45791445

在這裏插入圖片描述

編程要求(實現三個線程間隔運行)

在這裏插入圖片描述

實現代碼

package step3;

public class MyThread implements Runnable {   
 private String name;
    private Object prev;
    private Object self;

    private MyThread(String name, Object prev, Object self) {
        this.name = name;
        this.prev = prev;
        this.self = self;
    }


    public void run() {
        int count = 5;
        while (count > 0) {
            synchronized (prev) {
                synchronized (self) {
                    System.out.print(name);
                    count--;

                    self.notify();
                }
                try {
                    prev.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
        System.exit(0);
    }

    public static void main(String[] args) throws Exception {
        Object a = new Object();
        Object b = new Object();
        Object c = new Object();
        MyThread pa = new MyThread("E", c, a);
        MyThread pb = new MyThread("D", a, b);
        MyThread pc = new MyThread("U", b, c);


        new Thread(pa).start();
        Thread.sleep(100);  
        new Thread(pb).start();
        Thread.sleep(100);
        new Thread(pc).start();
        Thread.sleep(100);
    }

   
}

類似題目(實現兩個線程間隔運行)

在這裏插入圖片描述

實現代碼

public class MyThread implements Runnable {   
    private String name;   
    private Object prev;   
    private Object self;   
    private MyThread(String name, Object prev, Object self) {   
        this.name = name;   
        this.prev = prev;   
        this.self = self;   
    }   
    public void run() {   
        int count = 5;   
        while (count > 0) {   
            synchronized (prev) {   
                synchronized (self) {   
                    System.out.print(name);   
                    count--;  
                    self.notify();   
                }   
                try {   
                    prev.wait();   
                } catch (InterruptedException e) {   
                     e.printStackTrace();
                }   
            }   
        }  
        System.exit(0);//退出jvm
    }   
    public static void main(String[] args) throws Exception {   
        Object a = new Object();   
        Object b = new Object();   
        MyThread ta = new MyThread("A", b, a);   
        MyThread tb = new MyThread("B", a, b);   
        new Thread(ta).start();
        Thread.sleep(100);  //確保按順序A、B執行
        new Thread(tb).start();
        Thread.sleep(100);  
        }   
}

運行結果

ABABABABAB

上面兩道分別實現了將三個字母間隔輸出將兩個字母間隔輸出 。小白推薦大家可以去找找不同,這樣有助於理解,看看實現了間隔輸出的是那一部分,而實現了間隔輸出兩個或者三個的不同在哪裏?

下面是小白對於代碼的解釋。

對象鎖。同一時間只保證 一個線程訪問方法或變量。
在Java語言中,通過被關鍵字synchronized修飾的方法或synchronized語句塊實現對代碼的同步
包含在synchronized方法或語句塊中的代碼稱爲被同步的代碼(Synchronized Code)
當線程訪問被同步的代碼時,必須首先競爭代碼所屬的類的【對象上的鎖】,否則線程將等待(阻塞),直到鎖被釋放。

wait()就是說線程在獲取對象鎖後,主動釋放對象鎖,同時本線程休眠。直到有其它線程調用對象的notify()喚醒該線程,才能繼續獲取對象鎖,並繼續執行;

相應的notify()會喚醒其它等待該對象鎖的出於堵塞狀態的線程,並釋放該對象鎖操作。但有一點需要注意的是notify()調用後,並不是馬上就釋放對象鎖的,而是在相應的synchronized(){}語句塊執行結束,自動釋放鎖。然後JVM會在wait()對象鎖的線程中隨機選取一線程,賦予其對象鎖,喚醒線程,繼續執行。這樣就提供了在線程間隔同步、喚醒的操作。

Thread.sleep()與Object.wait()二者都可以暫停當前線程,釋放CPU控制權,主要的區別在於Object.wait()在釋放CPU同時,釋放了對象鎖的控制。

對第二道題進行講解

首先看一張圖,後面會用
在這裏插入圖片描述

解釋

  1. 首先

    代碼段1:MyThread ta = new MyThread(“A”, b, a);
    MyThread tb = new MyThread(“B”, a, b);
    實例化了對象ta和對象tb,由代碼段3可知
    此時在對象ta中name=“A” , prev=b , self=a;
    對象tb中name=“B”,prev=a,self=b;

代碼段2:new Thread(ta).start();

將這個對象創建一個新的線程(後面用線程ta來代表),並調用start方法將線程ta就緒,由於此時沒有其它線程,此線程或得CPU執行權開始運行,調用對應的run()方法。
在run方法中
所先,synchronized保證了只有一個線程能夠進入運行(實現原理),所以此時線程ta持有prev(此時是實例對象鎖b)進入運行,然後

   synchronized (self) {   
	                    System.out.print(name);   
	                    count--;  
	                    self.notify();   
	                }   

線程ta再次持有self(即實例對象鎖a)進入運行(此時線程ta持有實例對象鎖a,b),輸出name(即“A”),count-1,然後線程ta調用self.notify(),被轉化爲就緒狀態由於此時 被synchronized (self)包裹的代碼已經運行完畢,所以線程ta會釋放實例對象鎖a。 cpu再從同等優先級的線程中選擇某個線程去執行(還是有可能會選擇到此線程)。
然後線程ta調用了wait()方法,進入了堵塞狀態,暫時放棄對CPU的使用權,停止執行,就會釋放線程ta此時持有的對象鎖b。(爲什麼)然後調用notify()通知調用過wait方法的線程可以去參與獲得鎖的競爭了 。CPU則會再尋找一個新的線程去運行,而,

Thread.sleep(100);  //確保按順序A、B執行

這一段代碼是爲了確保線程ta出於堵塞狀態。
此時程序繼續向下運行,
在這裏插入圖片描述
tb線程被由新建狀態轉化爲就緒狀態,由於ta線程出於堵塞狀態,處於就緒狀態的只有tb線程。tb線程開始運行。相同的,tb線程開始調用run方法,由於對象鎖b沒用被其它線程持有,tb拿到了對象鎖b,然後進入 synchronized 修飾的代碼塊中。同理,由於對象鎖a沒被佔用,所以拿到對象鎖a。進入代碼段,輸出“B”,然後線程tb調用self.notify(),被轉化爲就緒狀態並且會喚醒等待對象鎖a的線程ta 由於此時 被synchronized (self)包裹的代碼已經運行完畢,所以線程tb會釋放實例對象鎖b。
在這裏插入圖片描述
然後,線程tb調用prev.wait();變爲堵塞狀態,釋放對象鎖a。
線程ta再次拿到了對象鎖b,已經對象鎖a。然後順利運行了下去,輸出“A”。當先後遇到self.notify();prev.wait();執行相同的操作,釋放其本身對象鎖a,喚醒出於堵塞狀態的線程tb,然後釋放實例對象鎖b。然後線程ta變爲堵塞狀態,等待線程tb運行到self.notify();時將它喚醒。

剩餘的過程就是上面的不斷循環,最終在count變爲0時,run()方法運行結束。線程死亡。

synchronized的實現原理
將synchronized作用於一個給定的實例對象(就是synchronized後面括號裏的,例如上面的代碼synchronized (prev)中的prev ),即當前實例對象就是鎖對象,每次當線程進入synchronized包裹的代碼塊時就會要求當前線程持有prev實例對象鎖,如果當前有其他線程正持有該對象鎖,那麼新到的線程就必須等待,這樣也就保證了每次只有一個線程執行i++;操作。

爲什麼線程ta會釋放它所持有的對象鎖
synchronized關鍵字修飾時,當此線程調用wait()方法後,就會釋放所持有的實例對象鎖。補充一下sleep()同樣是讓線程出於堵塞狀態,但是線程只會交出cpu的執行權,並不會釋放手中持有的鎖。


在這裏插入圖片描述
兄弟們,小白編寫不易。(這道題真的對於之前的小白而言,很燒腦。)希望各位兄弟們,點贊評論收藏加關注。小白在此謝謝各位老爺們。
在這裏插入圖片描述
對於白嫖的兄弟們,
在這裏插入圖片描述

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