中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

java怎么實現兩個線程按順序交替輸出1-100

發布時間:2022-01-15 17:44:51 來源:億速云 閱讀:209 作者:iii 欄目:大數據

這篇文章主要講解了“java怎么實現兩個線程按順序交替輸出1-100”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“java怎么實現兩個線程按順序交替輸出1-100”吧!

解法一

有了上面的思路,你肯定能快速寫出以下代碼:

public class PrintNumber extends Thread {
    private static int cnt = 0;
    private int id;  // 線程編號 
    public PrintNumber(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        while (cnt < 100) {
            while (cnt%2 == id) {
                cnt++;
                System.out.println("thread_" + id + " num:" + cnt);
            }
        }
    }

    public static void main(String[] args) {
        Thread thread0 = new PrintNumber(0);
        Thread thread1 = new PrintNumber(1);
        thread0.start();
        thread1.start();
    }
}

但當你實際運行后會發現!!!

thread_0 num:1
thread_0 num:3
thread_1 num:3
thread_1 num:5
thread_1 num:6
thread_0 num:5
thread_0 num:8
thread_0 num:9
thread_1 num:8
thread_0 num:11
thread_1 num:11
.........

不僅順序不對,還有重復和丟失!問題在哪?回到代碼中cnt++; System.out.println("thread_" + id + " num:" + cnt); 這兩行,它主要包含兩個動作,cnt++和輸出,當cnt++執行完成后可能就已經觸發了另一個線程的輸出。簡化下執行流程,每個時刻JVM有4個動作要執行。

  1. thread_0 cnt++

  2. thread_0 print

  3. thread_1 cnt++

  4. thread_1 print 根據Java as-if-serial語義,jvm只保證單線程內的有序性,不保證多線程之間的有序性,所以上面4個步驟的執行次序可能是 1 2 3 4,也可能是1 3 2 4,更可能是1 3 4 2,對于上面的代碼而言就是最終次序可能會發生變化。另外,cnt++ 可以拆解為兩行底層指令,tmp = cnt + 1; cnt = tmp,當兩個線程同時執行上述指令時就會面臨和1 2 3 4步驟同樣的問題,…… 沒錯,多線程下的行為,和你女朋友的心思一樣難以琢磨。 如何解決這個問題?解決方案本質上都是保證代碼執行順和我們預期的一樣就行,正確的解法一和后面幾個解法本質上都是同樣的原理,只是實現方式不一樣。

解法一正確的代碼如下:

public class PrintNumber extends Thread {
    private static AtomicInteger cnt = new AtomicInteger();
    private int id;
    public PrintNumber(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        while (cnt.get() <= 100) {
            while (cnt.get()%2 == id) {
                System.out.println("thread_" + id + " num:" + cnt.get());
                cnt.incrementAndGet();
            }
        }
    }

    public static void main(String[] args) {
        Thread thread0 = new PrintNumber(0);
        Thread thread1 = new PrintNumber(1);
        thread0.start();
        thread1.start();
    }
}

上面代碼通過AtomicInteger的incrementAndGet方法將cnt++的操作變成了一個原子操作,避免了多線程同時操作cnt導致的數據錯誤,另外,while (cnt.get()%2 == id也能保證只有單個線程才能進入while循環里執行,只有當前線程執行完inc后,下一個線程才能執行print,所以這個代碼是可以滿足我們交替輸出的需求的。 但是,這種方法很難駕馭,如果說我吧run函數寫成下面這樣:

    @Override
    public void run() {
        while (cnt.get() <= 100) {
            while (cnt.get()%2 == id) {
                cnt.incrementAndGet();
                System.out.println("thread_" + id + " num:" + cnt.get());
            }
        }
    }

只需要把print和cnt.incrementAndGet()換個位置,結果就完全不一樣了,先inc可能導致在print執行前下一個線程就進入執行改變了cnt的值,導致結果錯誤。另外這種方法其實也不是嚴格正確的,如果不是print而是其他類似的場景,可能會出問題,所以這種寫法強烈不推薦

解法二

事實上,我們只需要cnt++和print同時只有一個線程在執行就行了,所以我們可以簡單將方法一中錯誤的方案加上synchronized即可,代碼如下:

public class PrintNumber extends Thread {
    private static int cnt = 0;
    private int id;  // 線程編號
    public PrintNumber(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        while (cnt <= 100) {
            while (cnt%2 == id) {
                synchronized (PrintNumber.class) {
                    cnt++;
                    System.out.println("thread_" + id + " num:" + cnt);
                }
            }
        }
    }

    public static void main(String[] args) {
        Thread thread0 = new PrintNumber(0);
        Thread thread1 = new PrintNumber(1);
        thread0.start();
        thread1.start();
    }
}

這里我用了synchronized關鍵詞將cnt++和print包裝成了一個同步代碼塊,可以保證只有一個線程可以執行。這里不知道有沒有人會問,cnt需不需要聲明為volatile,我的回答是不需要,因為synchronized可以保證可見性。

大家有沒有發現,我上面代碼中一直都用了while (cnt.get()%2 == id)來判斷cnt是否是自己要輸出的數字,這就好比兩個小孩輪流報數,每個小孩都要耗費精力時不時看看是否到自己了,然后選擇是否報數,這樣顯然太低效了。能不能兩個小孩之間相互通知,一個小孩報完就通知下另一個小孩,然后自己休息,這樣明顯對雙方來說損耗的精力就少了很多。如果我們代碼能有類似的機制,這里就能損耗更少的無用功,提高性能。

這就得依賴于java的wait和notify機制,當一個線程執行完自己的工作,然后喚醒另一個線程,自己去休眠,這樣每個線程就不用忙等。代碼改造如下,這里我直接去掉了while (cnt.get()%2 == id)

    @Override
    public void run() {
        while (cnt <= 100) {
            synchronized (PrintNumber.class) {
                cnt++;
                System.out.println("thread_" + id + " num:" + cnt);
                PrintNumber.class.notify();
                try {
                    PrintNumber.class.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

解法三

能用synchronized的地方就能用ReentrantLock,所以解法三和解法二本質上是一樣的,就是把synchronized換成了lock而已,然后把wait和notify換成Condition的signal和await,改造后的代碼如下:

public class PrintNumber extends Thread {
    private static Lock lock = new ReentrantLock();
    private static Condition condition = lock.newCondition();
    private int id;
    private static int cnt = 0;
    public PrintNumber(int id) {
        this.id = id;
    }

    private static void print(int id) {

    }

    @Override
    public void run() {
        while (cnt <= 100) {
            lock.lock();
            System.out.println("thread_" + id + " num:" + cnt);
            cnt++;
            condition.signal();
            try {
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        Thread thread0 = new PrintNumber(0);
        Thread thread1 = new PrintNumber(1);
        thread0.start();
        thread1.start();
    }
}

感謝各位的閱讀,以上就是“java怎么實現兩個線程按順序交替輸出1-100”的內容了,經過本文的學習后,相信大家對java怎么實現兩個線程按順序交替輸出1-100這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

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

AI

彭州市| 无极县| 都江堰市| 黄骅市| 临城县| 朝阳市| 磐安县| 南丰县| 鄂托克前旗| 屯门区| 湘乡市| 望都县| 永川市| 榆树市| 攀枝花市| 长乐市| 邯郸县| 越西县| 河曲县| 修文县| 马公市| 惠安县| 铅山县| 宜兰县| 涞源县| 农安县| 奈曼旗| 阿尔山市| 阿巴嘎旗| 嵊州市| 锡林浩特市| 许昌市| 林口县| 慈利县| 伊春市| 河间市| 曲靖市| 绩溪县| 隆尧县| 黔西县| 武冈市|