国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

Java多線(xiàn)程之線(xiàn)程同步

發(fā)布時(shí)間:2021-07-17 21:51 來(lái)源:腳本之家 閱讀:0 作者:IT爛筆頭 欄目: 編程語(yǔ)言 歡迎投稿:712375056

volatile

先看個(gè)例子

class Test {
		// 定義一個(gè)全局變量
    private boolean isRun = true;
 
	  // 從主線(xiàn)程調用發(fā)起
    public void process() {
        test();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        stop();
    }
		// 啟動(dòng)一個(gè)子線(xiàn)程循環(huán)讀取isRun
    private void test() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (isRun) {
									// 疑問(wèn),如果我這里有一些打印的語(yǔ)句或者線(xiàn)程睡眠的語(yǔ)句,子線(xiàn)程在
									// 主線(xiàn)程將isRun改為false的時(shí)候,就會(huì )跳出死循環(huán),反之,如果循環(huán)體
									// 內是空的,就算在主線(xiàn)程改了isRun的值,也無(wú)法及時(shí)跳出循環(huán),why?
									// 當然,如果將isRun變量使用volatile修飾就沒(méi)有此問(wèn)題
                }
            }
        }).start();
    }
 
    private void stop() {
        isRun = false;
    }
}

有一點(diǎn)是一定的,就是子線(xiàn)程訪(fǎng)問(wèn)isRun的時(shí)候會(huì )拷貝一份放到自己的線(xiàn)程(工作內存)里,這樣在讀寫(xiě)的時(shí)候可能就不會(huì )和外面isRun的值實(shí)時(shí)是匹配上的。所以就會(huì )出現意想不到的問(wèn)題。

所以我們使用volatile修飾,這樣當有多線(xiàn)程同時(shí)訪(fǎng)問(wèn)一個(gè)變量時(shí),都會(huì )自動(dòng)同步一下。顯然這樣會(huì )帶來(lái)一定的性能損失,但是如果確實(shí)需要還是要這么做的。

但是,有一個(gè)問(wèn)題來(lái)了,使用volatile一定能就可解決多線(xiàn)程同步的問(wèn)題了嗎?那我們看下面這個(gè)例子:

class TestSynchronize {
 
		// 使用volatile修飾的變量
    private volatile int x = 0;
 
    private void add() {
        x++;
    }
 
    public void test() {
				// 啟動(dòng)第一個(gè)線(xiàn)程,進(jìn)行100萬(wàn)次自加
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i=0; i< 1_000_000; i++) {
                    add();
                }
                System.out.println("第一個(gè)線(xiàn)程x=" + x);
            }
        }).start();
				// 啟動(dòng)第二個(gè)線(xiàn)程,進(jìn)行100萬(wàn)次自加
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i=0; i< 1_000_000; i++) {
                    add();
                }
                System.out.println("第二個(gè)線(xiàn)程x=" + x);
            }
        }).start();
    }
}

我們希望的結果是,最后一個(gè)執行完的線(xiàn)程應該是在2_000_000,但是只要你實(shí)際測下就發(fā)現并不是這樣,因為volatile只能保證可見(jiàn)性,但是只要涉及多線(xiàn)程我們一定還聽(tīng)說(shuō)過(guò)原子性這個(gè)概念。什么是可見(jiàn)性:

可見(jiàn)性:對于多個(gè)線(xiàn)程都在訪(fǎng)問(wèn)的變量,當有個(gè)線(xiàn)程在修改的時(shí)候,它會(huì )保證會(huì )將修改的值更新到內存中,而不是只在工作線(xiàn)程中修改,這樣當別的線(xiàn)程訪(fǎng)問(wèn)的時(shí)候也會(huì )去內存中取最新的值,這樣就能保證訪(fǎng)問(wèn)到的值是最新的。

那什么又是原子性呢:

原子性:就是一個(gè)操作或者多個(gè)操作要么都執行,要么都不執行,不會(huì )存在執行一半會(huì )被打斷。

在Java中,對基本數據類(lèi)型變量的讀取和賦值操作是原子性的。但是上述代碼中的x++;顯然不是原子操作,可以拆解為:

int temp = x + 1;
x = temp;

那么這就為多線(xiàn)程操作帶來(lái)不確定性,

1、開(kāi)始x初始值為0,

2、當線(xiàn)程A調用add()函數時(shí),執行到temp=x+1;這一行時(shí)被中斷了,

3、此時(shí)切換到線(xiàn)程B的add()函數,線(xiàn)程B完整執行完兩行代碼后,x = 1了,

4、這個(gè)時(shí)候線(xiàn)程B又完整的執行了一遍add方法,那么x=2了,

5、此時(shí)發(fā)生了線(xiàn)程切換,切換到A執行,A接著(zhù)上次的執行的語(yǔ)句,temp = 1了,接下來(lái)執行x = temp;語(yǔ)句將1賦值給了x。

可是本來(lái)x都被B線(xiàn)程加到2了,這下又回去了,經(jīng)歷A和B線(xiàn)程一共三次add()操作,結果x的值只是1。

這就解釋了上面那段代碼中,兩個(gè)線(xiàn)程分別加了100萬(wàn)次后,結果最后一個(gè)執行完的線(xiàn)程打印的卻并不是200萬(wàn)。原因就是add()里面的操作并不是原子性的,而volatile只能保證可見(jiàn)性,不能保證原子性

當然,僅針對上面的按理我們可以將int x = 0;換一種類(lèi)型聲明,比如使用AtomicInteger x = new AtomicInteger(0);然后將x++改成x.incrementAndGet();這樣也能保證原子性,確保多線(xiàn)程操作后數據是符合期望的。

除了針對基本數據類(lèi)型的,還有對引用操作原子化的,AtomicReference<V>

synchronized

當synchronized修飾一個(gè)方法時(shí),那么同一時(shí)間只有一個(gè)線(xiàn)程可以訪(fǎng)問(wèn)此方法,如果有多個(gè)方法都被synchronized修飾的話(huà),當一個(gè)線(xiàn)程訪(fǎng)問(wèn)了其中一個(gè)方法,別的線(xiàn)程就無(wú)法訪(fǎng)問(wèn)其他被synchronized修飾的方法。

相當于有一個(gè)監視器,當一個(gè)線(xiàn)程訪(fǎng)問(wèn)某個(gè)方法,其他線(xiàn)程想訪(fǎng)問(wèn)別的方法時(shí),需要和同一個(gè)監視器做確認,這么做看起來(lái)不太合理,其實(shí)也是合理的,比如有兩方法都可能對同一個(gè)變量做操作,兩個(gè)線(xiàn)程能同時(shí)訪(fǎng)問(wèn)兩個(gè)方法,這樣數據還是會(huì )發(fā)生錯亂。

當然,我們就有兩個(gè)方法支持同步訪(fǎng)問(wèn)的場(chǎng)景的,只要我們自己確認兩個(gè)方法不會(huì )存在數據上的錯亂,我們可以為每個(gè)方法指定自己的監視器,在默認情況下是當前類(lèi)的對象(this)。

我們分別為setName();和其他兩個(gè)方法指定了不同的monitor(監視器),這樣當線(xiàn)程A訪(fǎng)問(wèn)上面兩個(gè)方法的時(shí)候,線(xiàn)程B想訪(fǎng)問(wèn)方法setName也是不受影響的:

接下來(lái)我們看我們經(jīng)常寫(xiě)的另一個(gè)例子,單例模式:

class TestInstance {
    private TestInstance(){}
    
    private static TestInstance sInstance;
    
    public static TestInstance newInstance() {
				**// ② 這里判空的目的?**
        if (sInstance == null) {
						**// ① 為什么鎖加在這里?**
            synchronized (TestInstance.class) {
								**// ③ 這里判空的目的?**
                if (sInstance == null) {
                    sInstance = new TestInstance();
                }
            }
        }
        return sInstance;
    }
}

我們來(lái)依次搞清楚上面的三個(gè)問(wèn)題,

①鎖為什么加在里面而不是在方法上加鎖,因為加鎖后會(huì )帶來(lái)性能上的損失的,單例對象只會(huì )創(chuàng )建一次,沒(méi)必要在實(shí)例已經(jīng)有的時(shí)候獲取單例時(shí)還加鎖,對性能是浪費。

②第一個(gè)判空的目的就是在已經(jīng)創(chuàng )建過(guò)實(shí)例之后的獲取操作,不用再經(jīng)過(guò)synchronized判斷,這樣更快。

③最后一個(gè)判空就是防止多個(gè)線(xiàn)程都會(huì )調到創(chuàng )建實(shí)例的操作。

到此這篇關(guān)于Java多線(xiàn)程之線(xiàn)程同步的文章就介紹到這了,更多相關(guān)Java線(xiàn)程同步內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自本網(wǎng)站內容采集于網(wǎng)絡(luò )互聯(lián)網(wǎng)轉載等其它媒體和分享為主,內容觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如侵犯了原作者的版權,請告知一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容,聯(lián)系我們QQ:712375056,同時(shí)歡迎投稿傳遞力量。

日韩AV无码精品一二三区| 黑人教练与娇妻H系列| 国产精品久久久久蜜芽| 国产成人AV综合久久| 国产在线乱码一区二区三区| 国偷自产视频一区二区久|