java并發(fā)

并發(fā)核心理論

  • 1.共享性: 數(shù)據(jù)共享是線程安全問題的主要問題之一

  • 2.互斥性: 資源互斥指同一時間內(nèi)值允許一個訪問者訪問.通常情況修改數(shù)據(jù)是互斥性晦譬,讀數(shù)據(jù)不要求.因此我們有共享鎖和互斥鎖.也叫讀鎖和寫鎖.

  • 3.原子性:指操作是一個獨(dú)立右冻,不可分割的整體.操作不會中斷纯出,數(shù)據(jù)不會執(zhí)行一半的時候被其他操作修改.例如一條指令就是最基本的原子.但是 i++,就分了好幾次操作:

    讀取i=1值 -->∨航睢2.i=1+1 --->3.存儲i=2

  • 4.可見性:


    image.png

    每個線程都有自己的工作內(nèi)存,對于共享變量梳码,先拿到變量的副本隐圾,對副本進(jìn)行操作,在某一個時間在把副本的值同步到共享變量.這樣導(dǎo)致边翁,有可能線程1修改了共享變量的值翎承,某一時間線程2可能拿不到最新的值.

  • 5.有序性:為了提高性能,編譯器和處理器可能會對指令進(jìn)行重排.

synchronized及其實(shí)現(xiàn)原理

synchronized 作用:

  • 1.確保線程互斥的訪問同步代碼
  • 2.保證共享變量能及時可見
  • 3.解決重排序問題

synchronize 的一般用法符匾,對象鎖,類鎖.(synchronized Object,synchronized 方法)
對class文件反編譯后的匯編語言如下:


image.png

image.png

說明synchronized 是在對象前面加了 monitor .
Monitorenter : 對象都享有一個monitor
1.線程一旦進(jìn)入monitor瘩例,monitor值1啊胶,線程為monitor擁有者.
2.其他線程到了這里,會進(jìn)行阻塞垛贤,直到monitor的值=0.
Monitorexit:monitor 變回0.說明其他線程可以擁有.

對方法加synchronized

image.png

image.png

Synchronized底層優(yōu)化(偏向鎖焰坪、輕量級鎖)
優(yōu)點(diǎn) 缺點(diǎn) 適用場景
偏向鎖 加鎖和解鎖不需要額外的消耗,和執(zhí)行非同步方法比僅存在納秒級的差距聘惦。 如果線程間存在鎖競爭某饰,會帶來額外的鎖撤銷的消耗。 適用于只有一個線程訪問同步塊場景善绎。
輕量級鎖 競爭的線程不會阻塞黔漂,提高了程序的響應(yīng)速度。 如果始終得不到鎖競爭的線程使用自旋會消耗CPU禀酱。 追求響應(yīng)時間炬守。同步塊執(zhí)行速度非常快剂跟。
重量級鎖 線程競爭不使用自旋减途,不會消耗CPU。 線程阻塞曹洽,響應(yīng)時間緩慢鳍置。 追求吞吐量。同步塊執(zhí)行時間較長送淆。

鎖的狀態(tài): 無鎖狀態(tài)税产,偏向鎖 ,輕量級鎖,重量級鎖
隨著鎖的競爭砖第,鎖的狀態(tài)可以升級撤卢,但是是單向的.從低到高,不會出現(xiàn)鎖的降級.
偏向鎖<輕量級鎖<重量級鎖


image.png

輕量級鎖:本意使用操作系統(tǒng)互斥量解決傳統(tǒng)的重量級鎖的性能問題.
使用場景:線程交替執(zhí)行同步塊.如果同一時刻訪問同一鎖的情況梧兼,輕量級鎖會升級重量級鎖.
偏向鎖:用于一個線程執(zhí)行同步塊是提高性能.一旦出現(xiàn)多線程競爭放吩,升級為輕量級鎖.
總結(jié) :JDk中采用輕量級鎖和偏向鎖等對Synchronized的優(yōu)化,但是這兩種鎖也不是完全沒缺點(diǎn)的羽杰,比如競爭比較激烈的時候渡紫,不但無法提升效率,反而會降低效率考赛,因?yàn)槎嗔艘粋€鎖升級的過程惕澎,這個時候就需要通過-XX:-UseBiasedLocking來禁用偏向鎖。下面是這幾種鎖的對比:

優(yōu)點(diǎn) 缺點(diǎn) 適用場景
偏向鎖 加鎖和解鎖不需要額外的消耗颜骤,和執(zhí)行非同步方法比僅存在納秒級的差距唧喉。 如果線程間存在鎖競爭,會帶來額外的鎖撤銷的消耗忍抽。 適用于只有一個線程訪問同步塊場景八孝。
輕量級鎖 競爭的線程不會阻塞,提高了程序的響應(yīng)速度鸠项。 如果始終得不到鎖競爭的線程使用自旋會消耗CPU干跛。 追求響應(yīng)時間。同步塊執(zhí)行速度非乘畎恚快楼入。
重量級鎖 線程競爭不使用自旋,不會消耗CPU牧抽。 線程阻塞嘉熊,響應(yīng)時間緩慢。 追求吞吐量阎姥。同步塊執(zhí)行時間較長记舆。

Java并發(fā)編程:線程間的協(xié)作(wait/notify/sleep/yield/join)

線程有5中狀態(tài):
新建(New)-->準(zhǔn)備狀態(tài)(Runnable)-->運(yùn)行狀態(tài)(Running)-->阻塞(Blocking)-->死亡(Dead)

new :創(chuàng)建 new Thread();
Runnable:調(diào)用start(),進(jìn)入就緒狀態(tài),等待cpu分配資源呼巴,有系統(tǒng)運(yùn)行時線程來調(diào)度
Running:開始執(zhí)行Run方法
Blocking: 阻塞
Dead
Wait/notify/notifyAll()
wait() 當(dāng)前線程掛起泽腮,直到有其他線程調(diào)用notify(), notifyAll().
wait(long timeout) 當(dāng)前線程掛起,直到有其他線程調(diào)用notify(), notifyAll();或者等到timeout
wait(long timeout,int nanos)
notify() 喚醒指定線程 , notifyAll() 喚醒所有的線程
注意:wait 必須在synchronized 塊之內(nèi).
Thread.sleep(long sleeptime)
當(dāng)前線程暫停指定的時間(毫秒),而更深層次的區(qū)別在于sleep方法只是暫時讓出CPU的執(zhí)行權(quán)衣赶,并不釋放鎖.而wait方法則需要釋放鎖,(意味其他線程可以拿到鎖诊赊,進(jìn)行操作)

package jni.test.leon.javatest;
/**
 * Created by leon on 17-12-11.
 */
public class SleepTest {
    public synchronized void sleepMethod() {
        System.out.println("Sleep start-----");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Sleep end-----");
    }
    public  void waitMethod() {
        System.out.println("Wait start-----");
        synchronized (this) {
            try {
                wait(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("Wait end-----");
    }
    public static void main(String[] args) {
        final SleepTest test1 = new SleepTest();
        for (int i = 0; i < 3; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    test1.sleepMethod();
                }
            }).start();
        }
        try {
            Thread.sleep(10000);//暫停十秒,等上面程序執(zhí)行完成
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("-----分割線-----");
        final SleepTest test2 = new SleepTest();
        for (int i = 0; i < 3; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    test2.waitMethod();
                }
            }).start();
        }
    }
}
//output-----
Connected to the target VM, address: '127.0.0.1:38691', transport: 'socket'
Sleep start-----
Sleep end-----
Sleep start-----
Sleep end-----
Sleep start-----
Sleep end-----
-----分割線-----
Wait start-----
Wait start-----
Wait start-----
Wait end-----
Disconnected from the target VM, address: '127.0.0.1:38691', transport: 'socket'
Wait end-----
Wait end-----

Process finished with exit code 0

Thread.yeild()
當(dāng)前線程暫停府瞄,讓其他線程有機(jī)會執(zhí)行.(用的場景比較少碧磅,主要是調(diào)試)

package jni.test.leon.javatest;
/**
 * Created by leon on 17-12-11.
 */
public class YieldTest implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
            Thread.yield();
        }
    }
    public static void main(String[] args) {
        YieldTest runn = new YieldTest();
        Thread t1 = new Thread(runn, "FirstThread");
        Thread t2 = new Thread(runn, "SecondThread");
        t1.start();
        t2.start();
    }
}
//--output
FirstThread: 0
FirstThread: 1
SecondThread: 0
FirstThread: 2
SecondThread: 1
FirstThread: 3
SecondThread: 2
FirstThread: 4
SecondThread: 3
SecondThread: 4

Thread.join()/Thread.join(long waittime)/Thread.join(long waittime,int nano)
作用:父線程等待子線程執(zhí)行完之后在執(zhí)行碘箍,可以達(dá)到異步子線程,最后的同步.

package jni.test.leon.javatest;
/**
 * Created by leon on 17-12-11.
 */
public class JoinTest implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(10);
            System.out.println(Thread.currentThread().getName() + " start-----");
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + " end------");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        Thread test = null;
        for (int i = 0; i < 5; i++) {
            test = new Thread(new JoinTest());
            test.start();
        }
        //這里是阻塞
        try {
            test.join(); //調(diào)用join方法
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Finished~~~");
    }
}
//output------
Thread-0 start-----
Thread-1 start-----
Thread-2 start-----
Thread-3 start-----
Thread-4 start-----
Thread-0 end------
Thread-1 end------
Thread-2 end------
Thread-3 end------
Thread-4 end------
Finished~~~

總結(jié):
因?yàn)閣ait() /notify () 是對象monitor,所以當(dāng)wait的時候就monitorEnter,必須等待有monitorExit 才能釋放對象的擁有權(quán).所以鲸郊,一旦wait 了丰榴,其他的線程是無法獲得擁有權(quán),也就不能進(jìn)入.

而sleep, yield這些是Thread級別的方法秆撮,只是讓出cpu執(zhí)行權(quán)join四濒,調(diào)用的是wait 方法,所以是會進(jìn)行對象級別的monitor.

volatile的使用及其原理

我們知道 可見性职辨,有序性盗蟆,原子性 的問題.synchronized 就是用來解決這些問題的,但是synchronized是比較重量級舒裤,volatile是一個輕量級的解決有序性喳资,可見性,原子性的方案.
原理:
1.對有序性:
在解釋這個問題前腾供,我們先來了解一下Java中的happen-before規(guī)則仆邓,JSR 133中對Happen-before的定義如下:

Two actions can be ordered by a happens-before relationship.If one action happens before another, then the first is visible to and ordered before the second.

通俗一點(diǎn)說就是如果a happen-before b,則a所做的任何操作對b是可見的台腥。(這一點(diǎn)大家務(wù)必記住宏赘,因?yàn)閔appen-before這個詞容易被誤解為是時間的前后)。我們再來看看JSR 133中定義了哪些happen-before規(guī)則:

  • Each action in a thread happens before every subsequent action in that thread.
  • An unlock on a monitor happens before every subsequent lock on that monitor.
  • A write to a volatile field happens before every subsequent read of that volatile.
  • A call to start() on a thread happens before any actions in the started thread.
  • All actions in a thread happen before any other thread successfully returns from a join() on that thread.
  • If an action a happens before an action b, and b happens before an action c, then a happens before c.

翻譯過來為:

  • 同一個線程中的黎侈,前面的操作 happen-before 后續(xù)的操作。(即單線程內(nèi)按代碼順序執(zhí)行闷游。但是峻汉,在不影響在單線程環(huán)境執(zhí)行結(jié)果的前提下,編譯器和處理器可以進(jìn)行重排序脐往,這是合法的休吠。換句話說,這一是規(guī)則無法保證編譯重排和指令重排)业簿。
  • 監(jiān)視器上的解鎖操作 happen-before 其后續(xù)的加鎖操作.(Synchronized 規(guī)則)
  • 對volatile變量的寫操作 happen-before 后續(xù)的讀操作瘤礁。(volatile 規(guī)則)
  • 線程的start() 方法 happen-before 該線程所有的后續(xù)操作。(線程啟動規(guī)則)
  • 線程所有的操作 happen-before 其他線程在該線程上調(diào)用 join 返回成功后的操作梅尤。
  • 如果 a happen-before b柜思,b happen-before c,則a happen-before c(傳遞性)巷燥。
    這里我們主要看下第三條:volatile變量的保證有序性的規(guī)則<<Java并發(fā)編程:核心理論>>

2.可見性
使用 Volatile 關(guān)鍵字
1.修改時會強(qiáng)制修改主內(nèi)存的數(shù)據(jù)
2.修改變量后導(dǎo)致其他線程工作中內(nèi)存的值失效赡盘,所以需要重新讀取主內(nèi)存的數(shù)據(jù)
volatile 的使用場景比較有限:
1.該變量寫操作不依賴當(dāng)前值
2.該變量沒有包含在具有其他變量的不變式中(這個變量只有原子性操作,簡單說來就是 進(jìn)行賦值)

常用:
1.狀態(tài)標(biāo)記量

A:
volatile boolean flag = false;
 
while(!flag){
    doSomething();
}
 
public void setFlag() {
    flag = true;
}

B:
volatile boolean inited = false;
//線程1:
context = loadContext();  
inited = true;            
 
//線程2:
while(!inited ){
sleep()
}
doSomethingwithconfig(context);

2.double check

class Singleton{
    private volatile static Singleton instance = null;
 
    private Singleton() {
 
    }
 
    public static Singleton getInstance() {
        if(instance==null) {
            synchronized (Singleton.class) {
                if(instance==null)
                    instance = new Singleton();
            }
        }
        return instance;
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末缰揪,一起剝皮案震驚了整個濱河市陨享,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖抛姑,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赞厕,死亡現(xiàn)場離奇詭異,居然都是意外死亡定硝,警方通過查閱死者的電腦和手機(jī)皿桑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來喷斋,“玉大人唁毒,你說我怎么就攤上這事⌒亲Γ” “怎么了浆西?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長顽腾。 經(jīng)常有香客問我近零,道長,這世上最難降的妖魔是什么抄肖? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任久信,我火速辦了婚禮,結(jié)果婚禮上漓摩,老公的妹妹穿的比我還像新娘裙士。我一直安慰自己,他們只是感情好管毙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布腿椎。 她就那樣靜靜地躺著,像睡著了一般夭咬。 火紅的嫁衣襯著肌膚如雪啃炸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天卓舵,我揣著相機(jī)與錄音南用,去河邊找鬼。 笑死掏湾,一個胖子當(dāng)著我的面吹牛裹虫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播忘巧,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼恒界,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了砚嘴?” 一聲冷哼從身側(cè)響起十酣,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤涩拙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后耸采,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體兴泥,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年虾宇,在試婚紗的時候發(fā)現(xiàn)自己被綠了搓彻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡嘱朽,死狀恐怖旭贬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情搪泳,我是刑警寧澤稀轨,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站岸军,受9級特大地震影響奋刽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜艰赞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一佣谐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧方妖,春花似錦狭魂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至仔役,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間是己,已是汗流浹背又兵。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留卒废,地道東北人沛厨。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像摔认,于是被迫代替她去往敵國和親逆皮。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容