java 多線程同步和異步詳解

隨著互聯(lián)網(wǎng)的發(fā)展,網(wǎng)絡流量越來越大翅溺,對web的性能挑戰(zhàn)越來越大卦绣,多線程的同步和異步則應運而生街望。

首先說一個在業(yè)界容易理解混淆的兩個概念:同步和異步
在說多線程是同步和異步:指的是對“共享資源”訪問的同步和異步。
比如兩個線程同時訪問某個static全局變量踱稍,這個時候就可能導致全局變量混亂曲饱。
在說ajax時說的同步和異步:值得時“行為”的同步和異步。比如在加載一個頁面的時候珠月,理論是后一個操作要等前一個操作執(zhí)行完畢返回之后扩淀,才可以執(zhí)行下一步,但是這樣在加載頁面的時候啤挎,給用戶的體驗十分的差驻谆,這個時候,ajax就可以異步的進行其他的操作(行為)庆聘。來加快頁面的相應速度胜臊。

這兩個同步和異步的含義有所不同,在業(yè)內(nèi)討論問題的時候伙判,要根據(jù)場景知道說的異步是什么含義象对。

下面講解多線程線程的同步和異步:

1,多線程并發(fā)時宴抚,多個線程同時請求同一個資源勒魔,必然導致此資源的數(shù)據(jù)不安全,A線程修改了B線
程的處理的數(shù)據(jù)酱塔,而B線程又修改了A線程處理的數(shù)理沥邻。顯然這是由于全局資源造成的,有時為了解
決此問題羊娃,優(yōu)先考慮使用局部變量唐全,退而求其次使用同步代碼塊,出于這樣的安全考慮就必須犧牲
系統(tǒng)處理性能蕊玷,加在多線程并發(fā)時資源掙奪最激烈的地方邮利,這就實現(xiàn)了線程的同步機制
同步:A線程要請求某個資源,但是此資源正在被B線程使用中垃帅,因為同步機制存在延届,A線程請求
不到,怎么辦贸诚,A線程只能等待下去
異步:A線程要請求某個資源方庭,但是此資源正在被B線程使用中厕吉,因為沒有同步機制存在,A線程
仍然請求的到械念,A線程無需等待

顯然头朱,同步最最安全,最保險的龄减。而異步不安全项钮,容易導致死鎖,這樣一個線程死掉就會導致整個
進程崩潰希停,但沒有同步機制的存在烁巫,性能會有所提升

java中實現(xiàn)多線程
1)繼承Thread,重寫里面的run方法
2)實現(xiàn)runnable接口

比較推薦后者,第一宠能,java沒有單繼承的限制第二亚隙,還可以隔離代碼

線程池
要知道在計算機中任何資源的創(chuàng)建,包括線程棍潘,都需要消耗系統(tǒng)資源的恃鞋。在WEB服務中,對于web服
務器的響應速度必須要盡可能的快亦歉,這就容不得每次在用戶提交請求按鈕后恤浪,再創(chuàng)建線程提供服務
。為了減少用戶的等待時間肴楷,線程必須預先創(chuàng)建水由,放在線程池中,線程池可以用HashTable這種數(shù)
據(jù)結構來實現(xiàn)(或者最新的同步包下面的一些組建實現(xiàn))赛蔫,看了Apach HTTP服務器的線程池的源代碼砂客,用是就是HashTable,KEY用線程對象,
value 用ControlRunnable呵恢,ControlRunnable是線程池中唯一能干活的線程鞠值,是它指派線程池中的
線程對外提供服務。
出于安全考慮渗钉,Apach HTTP服務器的線程池它是同步的彤恶。聽說weblogic有異步的實現(xiàn)方式,沒有研
究過鳄橘,不敢確定



一声离、關鍵字:
thread(線程)、thread-safe(線程安全)瘫怜、intercurrent(并發(fā)的)
synchronized(同步的)术徊、asynchronized(異步的)、
volatile(易變的)鲸湃、atomic(原子的)赠涮、share(共享)
二子寓、總結背景:
一次讀寫共享文件編寫,嚯世囊,好家伙别瞭,竟然揪出這些零碎而又是一路的知識點。于是乎株憾,Google和
翻閱了《Java參考大全》、《Effective Java Second Edition》晒衩,特此總結一下供日后工作學習參
考嗤瞎。
三、概念:
1听系、 什么時候必須同步贝奇?什么叫同步?如何同步靠胜?
要跨線程維護正確的可見性掉瞳,只要在幾個線程之間共享非 final 變量,就必須使用
synchronized(或 volatile)以確保一個線程可以看見另一個線程做的更改浪漠。
為了在線程之間進行可靠的通信陕习,也為了互斥訪問,同步是必須的址愿。這歸因于java語言規(guī)范的內(nèi)存
模型该镣,它規(guī)定了:一個線程所做的變化何時以及如何變成對其它線程可見。
因為多線程將異步行為引進程序响谓,所以在需要同步時损合,必須有一種方法強制進行。例如:如果2個線
程想要通信并且要共享一個復雜的數(shù)據(jù)結構娘纷,如鏈表嫁审,此時需要確保它們互不沖突,也就是必須阻
止B線程在A線程讀數(shù)據(jù)的過程中向鏈表里面寫數(shù)據(jù)(A獲得了鎖赖晶,B必須等A釋放了該鎖)律适。
為了達到這個目的,java在一個舊的的進程同步模型——監(jiān)控器(Monitor)的基礎上實現(xiàn)了一個巧
妙的方案:監(jiān)控器是一個控制機制嬉探,可以認為是一個很小的擦耀、只能容納一個線程的盒子,一旦一個
線程進入監(jiān)控器涩堤,其它的線程必須等待眷蜓,直到那個線程退出監(jiān)控為止。通過這種方式胎围,一個監(jiān)控器
可以保證共享資源在同一時刻只可被一個線程使用吁系。這種方式稱之為同步德召。(一旦一個線程進入一
個實例的任何同步方法,別的線程將不能進入該同一實例的其它同步方法汽纤,但是該實例的非同步方
法仍然能夠被調(diào)用)上岗。
錯誤的理解:同步嘛,就是幾個線程可以同時進行訪問蕴坪。
同步和多線程關系:沒多線程環(huán)境就不需要同步;有多線程環(huán)境也不一定需要同步肴掷。
鎖提供了兩種主要特性:互斥(mutual exclusion)和可見性(visibility)。
互斥即一次只允許一個線程持有某個特定的鎖背传,因此可使用該特性實現(xiàn)對共享數(shù)據(jù)的協(xié)調(diào)訪問協(xié)議
呆瞻,這樣,一次就只有一個線程能夠使用該共享數(shù)據(jù)径玖。
可見性要更加復雜一些痴脾,它必須確保釋放鎖之前對共享數(shù)據(jù)做出的更改對于隨后獲得該鎖的另一個
線程是可見的 —— 如果沒有同步機制提供的這種可見性保證,線程看到的共享變量可能是修改前
的值或不一致的值梳星,這將引發(fā)許多嚴重問題
小結:為了防止多個線程并發(fā)對同一數(shù)據(jù)的修改赞赖,所以需要同步,否則會造成數(shù)據(jù)不一致(就是所
謂的:線程安全冤灾。如java集合框架中Hashtable和Vector是線程安全的前域。我們的大部分程序都不是線
程安全的,因為沒有進行同步瞳购,而且我們沒有必要话侄,因為大部分情況根本沒有多線程環(huán)境)。

2学赛、 什么叫原子的(原子操作)年堆?
Java原子操作是指:不會被打斷地的操作。(就是做到互斥和可見性盏浇?1渖ァ)
那難道原子操作就可以真的達到線程安全同步效果了嗎?實際上有一些原子操作不一定是線程安全
的绢掰。
那么痒蓬,原子操作在什么情況下不是線程安全的呢?也許是這個原因導致的:java線程允許線程在自
己的內(nèi)存區(qū)保存變量的副本滴劲。允許線程使用本地的私有拷貝進行工作而非每次都使用主存的值是為
了提高性能(本人愚見:雖然原子操作是線程安全的攻晒,可各線程在得到變量(讀操作)后,就是各
自玩弄自己的副本了班挖,更新操作(寫操作)因未寫入主存中鲁捏,導致其它線程不可見)。
那該如何解決呢萧芙?因此需要通過java同步機制给梅。
在java中假丧,32位或者更少位數(shù)的賦值是原子的。在一個32位的硬件平臺上动羽,除了double和long
型的其它原始類型通常都是使用32位進行表示包帚,而double和long通常使用64位表示。另外运吓,對象引
用使用本機指針實現(xiàn)渴邦,通常也是32位的。對這些32位的類型的操作是原子的拘哨。
這些原始類型通常使用32位或者64位表示几莽,這又引入了另一個小小的神話:原始類型的大小是
由語言保證的。這是不對的宅静。java語言保證的是原始類型的表數(shù)范圍而非JVM中的存儲大小。因此站欺,
int型總是有相同的表數(shù)范圍姨夹。在一個JVM上可能使用32位實現(xiàn),而在另一個JVM上可能是64位的矾策。在
此再次強調(diào):在所有平臺上被保證的是表數(shù)范圍磷账,32位以及更小的值的操作是原子的。

3贾虽、 Java同步機制有4種實現(xiàn)方式:(部分引用網(wǎng)上資源)
① ThreadLocal ② synchronized( ) ③ wait() 與 notify() ④ volatile
目的:都是為了解決多線程中的對同一變量的訪問沖突
ThreadLocal
ThreadLocal 保證不同線程擁有不同實例逃糟,相同線程一定擁有相同的實例,即為每一個使用該
變量的線程提供一個該變量值的副本蓬豁,每一個線程都可以獨立改變自己的副本绰咽,而不是與其它線程
的副本沖突。
優(yōu)勢:提供了線程安全的共享對象
與其它同步機制的區(qū)別:同步機制是為了同步多個線程對相同資源的并發(fā)訪問地粪,是為了多個線程之
間進行通信取募;而 ThreadLocal 是隔離多個線程的數(shù)據(jù)共享,從根本上就不在多個線程之間共享資源
蟆技,這樣當然不需要多個線程進行同步了玩敏。
volatile
volatile 修飾的成員變量在每次被線程訪問時,都強迫從共享內(nèi)存中重讀該成員變量的值质礼。
而且旺聚,當成員變量發(fā)生變化時,強迫線程將變化值回寫到共享內(nèi)存眶蕉。
優(yōu)勢:這樣在任何時刻砰粹,兩個不同的線程總是看到某個成員變量的同一個值。
緣由:Java 語言規(guī)范中指出妻坝,為了獲得最佳速度伸眶,允許線程保存共享成員變量的私有拷貝惊窖,而
且只當線程進入或者離開同步代碼塊時才與共享成員變量的原始值對比。這樣當多個線程同時與某
個對象交互時厘贼,就必須要注意到要讓線程及時的得到共享成員變量的變化界酒。而 volatile 關鍵字就
是提示 VM :對于這個成員變量不能保存它的私有拷貝,而應直接與共享成員變量交互嘴秸。
使用技巧:在兩個或者更多的線程訪問的成員變量上使用 volatile 毁欣。當要訪問的變量已在
synchronized 代碼塊中,或者為常量時岳掐,不必使用凭疮。
線程為了提高效率,將某成員變量(如A)拷貝了一份(如B)串述,線程中對A的訪問其實訪問的
是B执解。只在某些動作時才進行A和B的同步,因此存在A和B不一致的情況纲酗。volatile就是用來避免這種
情況的衰腌。 volatile告訴jvm,它所修飾的變量不保留拷貝觅赊,直接訪問主內(nèi)存中的(讀操作多時使用
較好右蕊;線程間需要通信,本條做不到)
Volatile 變量具有 synchronized 的可見性特性吮螺,但是不具備原子特性饶囚。這就是說線程能夠自
動發(fā)現(xiàn) volatile 變量的最新值。Volatile 變量可用于提供線程安全鸠补,但是只能應用于非常有限的
一組用例:多個變量之間或者某個變量的當前值與修改后值之間沒有約束萝风。
您只能在有限的一些情形下使用 volatile 變量替代鎖。要使 volatile 變量提供理
想的線程安全莫鸭,必須同時滿足下面兩個條件:
對變量的寫操作不依賴于當前值闹丐;該變量沒有包含在具有其他變量的不變式中。

sleep() vs wait()
sleep是線程類(Thread)的方法被因,導致此線程暫停執(zhí)行指定時間卿拴,把執(zhí)行機會給其他線程,但是監(jiān)
控狀態(tài)依然保持梨与,到時后會自動恢復堕花。調(diào)用sleep不會釋放對象鎖。
wait是Object類的方法粥鞋,對此對象調(diào)用wait方法導致本線程放棄對象鎖缘挽,進入等待此對象的等待鎖
定池,只有針對此對象發(fā)出notify方法(或notifyAll)后本線程才進入對象鎖定池準備獲得對象鎖
進入運行狀態(tài)。
(如果變量被聲明為volatile壕曼,在每次訪問時都會和主存一致苏研;如果變量在同步方法或者同步塊中
被訪問,當在方法或者塊的入口處獲得鎖以及方法或者塊退出時釋放鎖時變量被同步腮郊。)

四摹蘑、例子:

Demo1:
package test.thread;
 
 
class SynTest{
 
//非同步
static void method(Thread thread){
System.out.println("begin "+thread.getName());
try{
Thread.sleep(2000);
}catch(Exception ex){
ex.printStackTrace();
}
System.out.println("end "+thread.getName());
}
 
//同步方式一:同步方法
synchronized static void method1(Thread thread){//這個方法是同步的方法,每次只有一
個線程可以進來
System.out.println("begin "+thread.getName());
try{
Thread.sleep(2000);
}catch(Exception ex){
ex.printStackTrace();
}
System.out.println("end "+thread.getName());
}
 
//同步方式二:同步代碼塊
static void method2(Thread thread){
synchronized(SynTest.class) {
System.out.println("begin "+thread.getName());
try{
Thread.sleep(2000);
}catch(Exception ex){
ex.printStackTrace();
}
System.out.println("end "+thread.getName());
}
}
 
 
//同步方式三:使用同步對象鎖
private static Object _lock1=new Object();
private static byte _lock2[]={};//據(jù)說轧飞,此鎖更可提高性能衅鹿。源于:鎖的對象越小越好
static void method3(Thread thread){
synchronized(_lock1) {
System.out.println("begin "+thread.getName());
try{
Thread.sleep(2000);
}catch(Exception ex){
ex.printStackTrace();
}
System.out.println("end "+thread.getName());
}
}
 
public static void main(String[] args){
//啟動3個線程,這里用了匿名類
for(int i=0;i<3;i++){
new Thread(){
public void run(){
method(this);
//method1(this);
//method2(this);
//method3(this);
}
}.start();
}
}
}
 
 
 
 
 
 
Demo2:
package test.thread;
 
import com.util.LogUtil;
 
 
public class SynTest2 {
 
public static void main(String[] args){
Callme target=new Callme();
Caller ob1=new Caller(target,"Hello");
Caller ob2=new Caller(target,"Synchronized");
Caller ob3=new Caller(target,"World");
}
}
 
class Callme{
 
 
synchronized void test(){
LogUtil.log("測試是否是:一旦一個線程進入一個實例的任何同步方法过咬,別的線程將不能
進入該同一實例的其它同步方法大渤,但是該實例的非同步方法仍然能夠被調(diào)用");
}
 
void nonsynCall(String msg){
LogUtil.log("["+msg);
LogUtil.log("]");
}
 
synchronized void synCall(String msg){
LogUtil.logPrint("["+msg);
LogUtil.log("]");
}
}
 
class Caller implements Runnable{
String msg;
Callme target;
Thread t;
 
Caller(Callme target,String msg){
this.target=target;
this.msg=msg;
t=new Thread(this);
t.start();
}
 
public void run() {
// TODO Auto-generated method stub
//target.nonsynCall(msg);
target.synCall(msg);
target.test();
}
 
 
}
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市掸绞,隨后出現(xiàn)的幾起案子泵三,更是在濱河造成了極大的恐慌,老刑警劉巖衔掸,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件切黔,死亡現(xiàn)場離奇詭異,居然都是意外死亡具篇,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門凌埂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來驱显,“玉大人,你說我怎么就攤上這事瞳抓“R撸” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵孩哑,是天一觀的道長栓霜。 經(jīng)常有香客問我,道長横蜒,這世上最難降的妖魔是什么胳蛮? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮丛晌,結果婚禮上腰湾,老公的妹妹穿的比我還像新娘腊嗡。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布把跨。 她就那樣靜靜地躺著故硅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上桐经,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天,我揣著相機與錄音浙滤,去河邊找鬼阴挣。 笑死,一個胖子當著我的面吹牛瓷叫,可吹牛的內(nèi)容都是我干的屯吊。 我是一名探鬼主播,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼摹菠,長吁一口氣:“原來是場噩夢啊……” “哼盒卸!你這毒婦竟也來了?” 一聲冷哼從身側響起次氨,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤蔽介,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后煮寡,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體虹蓄,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年幸撕,在試婚紗的時候發(fā)現(xiàn)自己被綠了薇组。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡坐儿,死狀恐怖律胀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情貌矿,我是刑警寧澤炭菌,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站逛漫,受9級特大地震影響黑低,放射性物質發(fā)生泄漏。R本人自食惡果不足惜酌毡,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一克握、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧枷踏,春花似錦玛荞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽婴梧。三九已至,卻和暖如春客蹋,著一層夾襖步出監(jiān)牢的瞬間塞蹭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工讶坯, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留番电,地道東北人。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓辆琅,卻偏偏與公主長得像漱办,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子婉烟,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

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

  • 從三月份找實習到現(xiàn)在娩井,面了一些公司,掛了不少似袁,但最終還是拿到小米洞辣、百度、阿里昙衅、京東扬霜、新浪、CVTE而涉、樂視家的研發(fā)崗...
    時芥藍閱讀 42,240評論 11 349
  • 線程池ThreadPoolExecutor corepoolsize:核心池的大小著瓶,默認情況下,在創(chuàng)建了線程池之后...
    irckwk1閱讀 724評論 0 0
  • 剛才朋友在群里說“林丹出軌了。今年這是怎么了麦向?學習寫作開公號的人可不愁沒話題了瘟裸。” 我說“是呢诵竭,我都想寫了” 一個...
    Chris的另一個世界閱讀 574評論 1 1
  • 我的父母極其的重男輕女话告,在我2歲的時候父母為了生弟弟,把我拋棄了卵慰,被親生母親送到福利院沙郭,當時的我非常害怕,哇哇大哭...
    四月芳華閱讀 476評論 0 0
  • 大家好裳朋,馬某應好友明月懷之邀病线,來此地小酌一杯。 我會在此逐一發(fā)表我這幾年所作的原創(chuàng)內(nèi)容。 愿在今后的日子里送挑,能結識...
    Myron馬閱讀 362評論 2 5