Android 線程簡單分析(一)
Android 并發(fā)之synchronized鎖住的是代碼還是對象(二)
Android 并發(fā)之CountDownLatch垃帅、CyclicBarrier的簡單應(yīng)用(三)
Android 并發(fā)HashMap和ConcurrentHashMap的簡單應(yīng)用(四)(待發(fā)布)
Android 并發(fā)之Lock、ReadWriteLock和Condition的簡單應(yīng)用(五)
Android 并發(fā)之CAS(原子操作)簡單介紹(六)
Android 并發(fā)Kotlin協(xié)程的重要性(七)(待發(fā)布)
Android 并發(fā)之AsyncTask原理分析(八)(待發(fā)布)
Android 并發(fā)之Handler脱吱、Looper羡宙、MessageQueue和ThreadLocal消息機(jī)制原理分析(九)
Android 并發(fā)之HandlerThread和IntentService原理分析(十)
在java中多線程并發(fā)時,多個線程同時請求一個共享資源,必然會導(dǎo)致此資源的數(shù)據(jù)不安全抱完,可能我們就需要synchronized同步避免線程并發(fā)訪問共享資源導(dǎo)致數(shù)據(jù)不安全,但是synchronized鎖住的是代碼還是對象刃泡?
看一下栗子:
public class SynchronizedExample {
public synchronized void share() {
Log.e("tag", "share 開始執(zhí)行.....");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("tag", "share 結(jié)束.....");
}
}
class SynchronizedExampleThread extends Thread {
@Override
public void run() {
SynchronizedExample example = new SynchronizedExample();
example.share();
}
}
1巧娱、在SynchronizedExample 的share方法加synchronized 打印結(jié)果:
share 開始執(zhí)行.....
share 開始執(zhí)行.....
share 開始執(zhí)行.....
share 結(jié)束.....
share 結(jié)束.....
share 結(jié)束.....
可以看出來,上面的起動了三個線程烘贴,同時運行SynchronizedExample 類中的share方法禁添,雖然share方法加上了synchronized,但是還是同時運行起來桨踪,好像synchronized并沒起到同步的這用作用老翘?
再來改改,改成同步代碼塊锻离?
public void share() {
synchronized (this) {
Log.e("tag", "share 開始執(zhí)行.....");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("tag", "share 結(jié)束.....");
}
}
結(jié)果還是和上面的一樣
share 開始執(zhí)行.....
share 開始執(zhí)行.....
share 開始執(zhí)行.....
share 結(jié)束.....
share 結(jié)束.....
share 結(jié)束.....
在java中铺峭,synchronized(this)以及非static的synchronized方法,只能防止多個線程同時執(zhí)行同一個對象的同步代碼段汽纠,即同一個鎖監(jiān)視器卫键。
分析一下上面的代碼?
實際上虱朵,在java中莉炉,對于非static的synchronized方法或者是synchronized代碼塊钓账,鎖的就是對象本身也就是this。是不是有答案了呢袱?
synchronized鎖住的是括號里的對象官扣,而不是代碼。
當(dāng)synchronized鎖住一個對象后羞福,別的線程如果也想拿到這個對象的鎖惕蹄,就必須等待(同步隊列)這個線程執(zhí)行完成釋放鎖,才能再次給對象加鎖治专,這樣才達(dá)到線程同步的目的卖陵,如果不明白可以看Android 線程簡單分析(一)介紹,很詳細(xì)张峰。
所以我們在用synchronized關(guān)鍵字的時候泪蔫,能縮小代碼段的范圍就盡量縮小,能在代碼段上加同步就不要再整個方法上加同步喘批。這叫減小鎖的粒度撩荣,使代碼更大程度的并發(fā)。鎖的代碼段太長了饶深,別的線程是不是要等很久餐曹。
再看上面的代碼,每個線程中都new了一個SynchronizedExample 類的對象敌厘,也就是產(chǎn)生了三個SynchronizedExample 對象台猴,由于不是同一個對象,所以可以多線程同時運行synchronized方法或代碼段俱两。
驗證:
SynchronizedExample example = new SynchronizedExample();
class SynchronizedExampleThread extends Thread {
@Override
public void run() {
example.share();
}
}
結(jié)果:
share 開始執(zhí)行.....
share 結(jié)束.....
share 開始執(zhí)行.....
share 結(jié)束.....
share 開始執(zhí)行.....
share 結(jié)束.....
synchronized就起了作用饱狂。
為了在明確一點我在創(chuàng)建兩個SynchronizedExample 對象:
SynchronizedExample example = new SynchronizedExample();
SynchronizedExample example2 = new SynchronizedExample();
class SynchronizedExampleThread extends Thread {
@Override
public void run() {
example.share();
}
}
class SynchronizedExampleThread2 extends Thread {
@Override
public void run() {
example2.share2();
}
}
結(jié)果:
share 開始執(zhí)行.....
share2 開始執(zhí)行.....
share 結(jié)束.....
share 開始執(zhí)行.....
share2 結(jié)束.....
share2 開始執(zhí)行.....
share 結(jié)束.....
share 開始執(zhí)行.....
share2 結(jié)束.....
share2 開始執(zhí)行.....
share 結(jié)束.....
share2 結(jié)束.....
明顯同一個所得是同一個對象,即同一把鎖宪彩。
同一個對象每條線程進(jìn)入同步代碼塊休讳,其他線程將進(jìn)入同步隊列或鎖池中等待其他線程釋放鎖,synchronize屬于非公平鎖毯焕,在鎖池的等待的線程在線程1釋放鎖開始競爭鎖衍腥,所以synchronize鎖的是對象。
非公平鎖和公平鎖
1纳猫、非公平鎖:線程加鎖時直接嘗試獲取鎖婆咸,獲取不到就自動到隊尾等待,ReentrantLock(默認(rèn))和synchronize屬于非公平鎖芜辕。
2尚骄、公平鎖:加鎖前先查看是否有排隊等待的線程,有的話優(yōu)先處理排在前面的線程侵续,先來先得倔丈。樂觀鎖和悲觀鎖
1憨闰、悲觀鎖:假設(shè)一定會發(fā)生并發(fā)沖突,通過阻塞其他所有線程來保證數(shù)據(jù)的完整性需五,就是屬于悲觀主義鹉动,synchronize就是悲觀鎖。
2宏邮、樂觀鎖:假設(shè)不會發(fā)生并發(fā)沖突泽示,直接不加鎖去完成某項更新,如果沖突就返回失敗蜜氨,CAS機(jī)制屬于樂觀的械筛。