真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

java怎么實現(xiàn)兩個線程按順序交替輸出1-100

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

創(chuàng)新互聯(lián)公司是一家集網(wǎng)站建設(shè),柴桑企業(yè)網(wǎng)站建設(shè),柴桑品牌網(wǎng)站建設(shè),網(wǎng)站定制,柴桑網(wǎng)站建設(shè)報價,網(wǎng)絡(luò)營銷,網(wǎng)絡(luò)優(yōu)化,柴桑網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強企業(yè)競爭力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時我們時刻保持專業(yè)、時尚、前沿,時刻以成就客戶成長自我,堅持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實用型網(wǎng)站。

解法一

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

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();
    }
}

但當(dāng)你實際運行后會發(fā)現(xiàn)?。。?/p>

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
.........

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

  1. thread_0 cnt++

  2. thread_0 print

  3. thread_1 cnt++

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

解法一正確的代碼如下:

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導(dǎo)致的數(shù)據(jù)錯誤,另外,while (cnt.get()%2 == id也能保證只有單個線程才能進入while循環(huán)里執(zhí)行,只有當(dāng)前線程執(zhí)行完inc后,下一個線程才能執(zhí)行print,所以這個代碼是可以滿足我們交替輸出的需求的。 但是,這種方法很難駕馭,如果說我吧run函數(shù)寫成下面這樣:

    @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()換個位置,結(jié)果就完全不一樣了,先inc可能導(dǎo)致在print執(zhí)行前下一個線程就進入執(zhí)行改變了cnt的值,導(dǎo)致結(jié)果錯誤。另外這種方法其實也不是嚴格正確的,如果不是print而是其他類似的場景,可能會出問題,所以這種寫法強烈不推薦

解法二

事實上,我們只需要cnt++和print同時只有一個線程在執(zhí)行就行了,所以我們可以簡單將方法一中錯誤的方案加上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關(guān)鍵詞將cnt++和print包裝成了一個同步代碼塊,可以保證只有一個線程可以執(zhí)行。這里不知道有沒有人會問,cnt需不需要聲明為volatile,我的回答是不需要,因為synchronized可以保證可見性。

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

這就得依賴于java的wait和notify機制,當(dāng)一個線程執(zhí)行完自己的工作,然后喚醒另一個線程,自己去休眠,這樣每個線程就不用忙等。代碼改造如下,這里我直接去掉了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,所以解法三和解法二本質(zhì)上是一樣的,就是把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怎么實現(xiàn)兩個線程按順序交替輸出1-100”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對java怎么實現(xiàn)兩個線程按順序交替輸出1-100這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!


文章標題:java怎么實現(xiàn)兩個線程按順序交替輸出1-100
鏈接URL:http://weahome.cn/article/jsjojh.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部