国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

【Java并發(fā)編程的藝術(shù)】第二章讀書筆記之synchronized關(guān)鍵字

GT / 1058人閱讀

摘要:在之前的文章中學(xué)習(xí)了關(guān)鍵字,可以保證變量在線程間的可見性,但他不能真正的保證線程安全。線程執(zhí)行到指令時,將會嘗試獲取對象所對應(yīng)的的所有權(quán),即嘗試獲得對象的鎖。從可見性上來說,線程通過持有鎖的方式獲取變量的最新值。

在之前的文章中學(xué)習(xí)了volatile關(guān)鍵字,volatile可以保證變量在線程間的可見性,但他不能真正的保證線程安全。

/**
 * @author cenkailun
 * @Date 9/5/17
 * @Time 20:23
 */
public class ConcurrentAddWithVolatile implements Runnable {

    private static ConcurrentAddWithVolatile instance = new ConcurrentAddWithVolatile();
    private static volatile int i = 0;


    public static void increase() {
        i++;
    }

    public void run() {
        for (int j = 0; j < 1000000; j++) {
            i++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(instance,"線程1");
        Thread t2 = new Thread(instance, "線程2");
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }
}

如上述代碼所示,如果說兩個線程是正確的并發(fā)執(zhí)行的話,最后得到的結(jié)果應(yīng)該是2000000,但結(jié)果往往是小于2000000。那么這是為什么呢?

經(jīng)過閱讀書籍,可以得知,i++的這個操作,其實是要分成3步。

1. 讀取i的當(dāng)前值到操作棧
2. 對i的當(dāng)前值+1
3. 寫回i+1后的值

經(jīng)過了上述3步,才完成了i++ 的這個操作,volatile保證了寫回內(nèi)存后,i的最新值能夠被其他線程獲取,但i++的這三個動作不是一個整體,即不是原子操作,是可以被拆開的。

比如,線程1和2同時讀取了i為0,并各自在自己的線程中計算得到i=1,先后寫入這個i的值,導(dǎo)致雖然i++被執(zhí)行了兩次,但是實際i的值只增加了1。

如果要解決這個問題,就要保證多個線程在對i進(jìn)行++ 這個操作時完全同步,即i++的這三步是一起完成的,當(dāng)線程1在寫入時,其他線程不能讀也不能寫,因為在線程1寫完之前,其他線程讀到的肯定是一個過期的數(shù)據(jù)。

Java提供了synchronized來實現(xiàn)這個功能,保證多線程執(zhí)行時候的同步,某一時刻只有一個線程可以對synchronized關(guān)鍵字保護起來的區(qū)域進(jìn)行操作,相對于volatile來說是比較重量級的。

Java的synchronized關(guān)鍵字具體表現(xiàn)有以下三種形式:

作用于實例方法,鎖的是當(dāng)前實例對象。

作用于靜態(tài)方法,鎖的是當(dāng)前類。

作用于代碼塊,鎖的是Synchronized里配置的對象。

下面是一個示例,將synchronized作用于一個給定對象instance,每當(dāng)線程要進(jìn)入被包裹的代碼塊,會請求instance的鎖。如果有其他線程已經(jīng)持有了這把鎖,那么新到的線程就必須等待,這樣保證了每次只有一個線程會執(zhí)行i++操作。

/**
 * @author cenkailun
 * @Date 9/5/17
 * @Time 20:23
 */
public class ConcurrentAddWithVolatile implements Runnable {

    private static ConcurrentAddWithVolatile instance = new ConcurrentAddWithVolatile();
    private static volatile int i = 0;


    public static void increase() {
        i++;
    }

    public void run() {
        for (int j = 0; j < 1000000; j++) {
            synchronized (instance) {     //同步代碼塊
                i++;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(instance,"線程1");
        Thread t2 = new Thread(instance, "線程2");
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }
}

對于java中的代碼塊同步,JVM是基于進(jìn)入和退出Monitor對象來實現(xiàn)代碼塊同步的,將monitorenter指令插入到同步代碼塊的開始位置,monitorexit插入到方法結(jié)束處和異常處,每一個對象都有一個monitor與之對應(yīng),當(dāng)一個monitor被持有后,它將處于鎖定狀態(tài)。線程執(zhí)行到monitorenter指令時,將會嘗試獲取對象所對應(yīng)的monitor的所有權(quán),即嘗試獲得對象的鎖。

如下面字節(jié)碼所示,代表上文代碼中的同步代碼塊。

13: monitorenter
14: getstatic     #2                  // Field i:I
17: iconst_1
18: iadd
19: putstatic     #2                  // Field i:I
22: aload_2
23: monitorexit

對于實例方法或者靜態(tài)方法上加的synchronized關(guān)鍵字,在方法上會有一個標(biāo)志位代表,如下面字節(jié)碼所示。

public synchronized void increase();
flags: ACC_PUBLIC, ACC_SYNCHRONIZED

在我看來,synchronized相對于volatile的強大之處在于保證了線程安全性以及做到了線程同步,同時也能做到volatile提供的線程間可見性以及有序性。從可見性上來說,線程通過持有鎖的方式獲取變量的最新值。從有序性上來說,synchronized限制每次只有一個線程可以訪問同步的代碼,無論內(nèi)部指令順序如何被打亂,jvm會保證最終執(zhí)行的結(jié)果總是一樣,其他線程只能在獲得鎖后讀取結(jié)果數(shù)據(jù),不會讀到中間值,所以有序性問題也得到了解決。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/70341.html

相關(guān)文章

  • Java并發(fā)編程藝術(shù)二章讀書筆記volatile關(guān)鍵字

    摘要:目前看的部分主要是這個關(guān)鍵字。語言提供了,保證了所有線程能看到共享變量最新的值。前綴的指令在多核處理器下會做兩件事情將當(dāng)前處理器緩存行的數(shù)據(jù)寫回到系統(tǒng)內(nèi)存。 這一章節(jié)的話,主要是講一下在并發(fā)操作中常見的volatile、synchronized以及原子操作的相關(guān)知識。 目前看的部分主要是volatile這個關(guān)鍵字。 volatile 根據(jù)Java語言規(guī)范第3版中對volatile的定義...

    szysky 評論0 收藏0
  • Java并發(fā)編程藝術(shù)二章讀書筆記原子操作

    摘要:前言今天的筆記來了解一下原子操作以及中如何實現(xiàn)原子操作。概念原子本意是不能被進(jìn)一步分割的最小粒子,而原子操作意為不可被中斷的一個或一系列操作。處理器實現(xiàn)原子操作處理器會保證基本內(nèi)存操作的原子性。 showImg(https://segmentfault.com/img/bVVIRA?w=1242&h=536); 前言 今天的筆記來了解一下原子操作以及Java中如何實現(xiàn)原子操作。 概念 ...

    olle 評論0 收藏0
  • Java并發(fā)編程藝術(shù)】第一章讀書筆記

    摘要:前言并發(fā)編程的目的是讓程序跑的更快,但并不是啟動更多的線程,這個程序就跑的更快。盡可能降低上下文切換的次數(shù),有助于提高并發(fā)效率。死鎖并發(fā)編程中的另一挑戰(zhàn)是死鎖,會造成系統(tǒng)功能不可用。 前言 并發(fā)編程的目的是讓程序跑的更快,但并不是啟動更多的線程,這個程序就跑的更快。有以下幾種挑戰(zhàn)。 挑戰(zhàn)及方案 上下文切換 單核CPU上執(zhí)行多線程任務(wù),通過給每個線程分配CPU時間片的方式來實現(xiàn)這個機制。...

    馬忠志 評論0 收藏0
  • 那些年我看過書 —— 致敬我大學(xué)生活 —— Say Good Bye !

    摘要:開頭正式開啟我入職的里程,現(xiàn)在已是工作了一個星期了,這個星期算是我入職的過渡期,算是知道了學(xué)校生活和工作的差距了,總之,盡快習(xí)慣這種生活吧。當(dāng)時是看的廖雪峰的博客自己也用做爬蟲寫過幾篇博客,不過有些是在前人的基礎(chǔ)上寫的。 showImg(https://segmentfault.com/img/remote/1460000010867984); 開頭 2017.08.21 正式開啟我...

    xiaoqibTn 評論0 收藏0
  • 并發(fā)編程藝術(shù)

    摘要:假設(shè)不發(fā)生編譯器重排和指令重排,線程修改了的值,但是修改以后,的值可能還沒有寫回到主存中,那么線程得到就是很自然的事了。同理,線程對于的賦值操作也可能沒有及時刷新到主存中。線程的最后操作與線程發(fā)現(xiàn)線程已經(jīng)結(jié)束同步。 很久沒更新文章了,對隔三差五過來刷更新的讀者說聲抱歉。 關(guān)于 Java 并發(fā)也算是寫了好幾篇文章了,本文將介紹一些比較基礎(chǔ)的內(nèi)容,注意,閱讀本文需要一定的并發(fā)基礎(chǔ)。 本文的...

    curlyCheng 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<