JDK多線程基礎筆記(2):線程互斥與同步(synchronized)

線程互斥(線程安全)

synchronized 簡介

  1. synchronized 是 Java 內(nèi)建的同步機制,所以也有人稱其為 Intrinsic Locking唤崭,它提供了互斥的語義和可見性棕孙,當一個線程已經(jīng)獲取當前鎖時舔亭,其他試圖獲取的線程只能等待或者阻塞在那里

  2. synchronized 是 Java 中最為常用的同步方法之一,實現(xiàn)比較簡單蟀俊,代碼簡潔钦铺,可讀性和維護性較好

  3. 在JDK早期版本中,性能并不好肢预,只適合鎖競爭不是特別激烈的場合矛洞。目前隨著JVM進步得到很好的優(yōu)化,性能與重入鎖(ReentrantLock)差距縮小

  4. JDK源碼中 synchronized 的使用也有很多烫映,如同步容器 Hashtable, 同步包裝器(Synchronized Wrapper)沼本,我們可以調(diào)用 Collections 工具類提供的包裝方法,來獲取一個同步的包裝容器(如 Collections.synchronizedMap)但是它們都是利用非常粗粒度的同步方式锭沟,在高并發(fā)情況下抽兆,性能比較低下

  5. 更好的選擇是使用并發(fā)包(JUC)提供的線程安全容器,這些容器基本是使用重入鎖(ReentrantLock)實現(xiàn)族淮,synchronizedReentrantLock的比較辫红,見重入鎖(ReentrantLock)博文

synchronized 用法

  1. 鎖定對象:一定要同一個對象
  • synchronized 鎖定非靜態(tài)方法,這個等同于把方法全部語句用 synchronized 塊包起來祝辣。一個方法建議一個 synchronized 贴妻,不然容易產(chǎn)生死鎖
private synchronized void get(String name) {}
  • synchronized 鎖定業(yè)務類對象。一般使用synchronized(this)
private void get2(String name) {
    int len = name.length();
    synchronized (this) {
        for (int i = 0; i < len; i++) {
            System.out.print(name.charAt(i));
        }
        System.out.println();
    }
}
  1. synchronized 鎖定同步塊蝙斜。相比較與鎖定對象揍瑟,鎖定塊更加精確,減少了鎖范圍乍炉,效率更高绢片。
private void get3(String name) {
    ...
    synchronized (name) {
    
    }
    ...
}
  1. 鎖類:鎖定字節(jié)碼
  • 鎖靜態(tài)方法:等價與鎖定當前 Class 上
public synchronized static void get5(String name) {}
  • 鎖 Class 對象上滤馍。直接顯示鎖定字節(jié)碼,如
public static void get4(String name) {
    int len = name.length();
    synchronized (DataObject.class) {
        for (int i = 0; i < len; i++) {
            System.out.print(name.charAt(i));
        }
        System.out.println();
    }
}

線程同步(多線程復雜交互)

方法簡介

  1. synchronized: 保證線程安全底循,線程互斥

  2. wait: 可以讓線程等待當前對象上的通知(notify被調(diào)用)巢株,在wait的過程中,線程會釋放對象鎖熙涤,供其他線程使用阁苞。當接收到對象上的通知后(notify被調(diào)用),就能重新獲取對象的獨占鎖祠挫,并且繼續(xù)運行

  3. notify:可以喚醒一個等待在當前對象上的線程那槽。如果有多個線程等待,講隨機選擇一個

示例代碼

  1. 兩個線程:子線程循環(huán)3次等舔,主線程循環(huán)5次骚灸,然后子線程又循環(huán)3次,主線程5次慌植,如此一共循環(huán)2次
  • synchronized甚牲、waitnotify 操作的必須是同一個對象
public class ThreadCommunication {

    public static void main(String[] args) {
        Data data = new Data();
        new Thread(new MainThread(data), "thead 0 ").start();
        new Thread(new SubThread(data), "thead 1 ").start();
    }

    /**
     * 主線程
     */
    static class MainThread implements Runnable {

        private Data data;

        public MainThread(Data data) {
            this.data = data;
        }

        @Override
        public void run() {
            try {
                for (int i = 0; i < 2; i++) {
                    data.mainMethod();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 子線程
     */
    static class SubThread implements Runnable {

        private Data data;

        public SubThread(Data data) {
            this.data = data;
        }

        @Override
        public void run() {
            try {
                for (int i = 0; i < 2; i++) {
                    data.subMethod();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 業(yè)務對象
     */
    static class Data {
        /**
         * 默認子線程運行
         */
        private volatile boolean isSubRunnable = true;

        synchronized void mainMethod() throws InterruptedException {

            // 主線程先等待
            while (isSubRunnable) {
                this.wait();
            }


            for (int i = 0; i < 5; i++) {
                System.out.println("Main線程運行次數(shù) : " + i);
            }


            isSubRunnable = true;
            this.notify();
        }

        synchronized void subMethod() throws InterruptedException {

            // 一開始是true,先子線程運行
            while (!isSubRunnable) {
                this.wait();
            }

            for (int i = 0; i < 3; i++) {
                System.out.println("Sub線程運行次數(shù) : " + i);
            }

            isSubRunnable = false;
            this.notify();//喚醒
        }
    }
}

  1. 線程同步: 簡單實現(xiàn)一個阻塞隊列
public class CustomBlockQueue {
    private List<Object> list = new ArrayList<>();

    public synchronized Object pop() throws InterruptedException {
        // 如果隊列為空蝶柿,等待
        while (list.isEmpty()) {
            this.wait();
        }
        if (list.size() > 0) {
            // 隊列不為空丈钙,返回第一個對象
            return list.remove(0);
        } else {
            return null;
        }
    }

    public synchronized void put(Object object) {
        // 添加到隊列當中
        list.add(object);
        // 通知一個 pop()方法,可以取數(shù)據(jù)
        this.notify();
    }
}

synchronized 總結(jié)

  1. 代碼簡潔交汤,線程安全雏赦,性能靠譜,功能沒有 ReentrantLock 豐富
  2. 鎖對象芙扎、鎖代碼塊喉誊、鎖class類三種用法
  3. synchronized 加鎖的方法之間也是互斥的(隊列),原因在于鎖定的是同一個對象
  4. 只能保證線程安全纵顾,無法控制復雜邏輯的多線程交互,如需實現(xiàn)多線程交互栋盹,需要配合使用Object對象的 wait施逾、notify 方法

參考

  1. 源碼地址
  2. Java線程(一):傳統(tǒng)線程的實現(xiàn)、互斥與通信
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末例获,一起剝皮案震驚了整個濱河市汉额,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌榨汤,老刑警劉巖蠕搜,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異收壕,居然都是意外死亡妓灌,警方通過查閱死者的電腦和手機轨蛤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來虫埂,“玉大人祥山,你說我怎么就攤上這事〉舴” “怎么了缝呕?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長斧散。 經(jīng)常有香客問我供常,道長,這世上最難降的妖魔是什么鸡捐? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任栈暇,我火速辦了婚禮,結(jié)果婚禮上闯参,老公的妹妹穿的比我還像新娘瞻鹏。我一直安慰自己,他們只是感情好鹿寨,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布新博。 她就那樣靜靜地躺著,像睡著了一般脚草。 火紅的嫁衣襯著肌膚如雪赫悄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天馏慨,我揣著相機與錄音埂淮,去河邊找鬼。 笑死写隶,一個胖子當著我的面吹牛倔撞,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播慕趴,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼痪蝇,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了冕房?” 一聲冷哼從身側(cè)響起躏啰,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎耙册,沒想到半個月后给僵,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡详拙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年帝际,在試婚紗的時候發(fā)現(xiàn)自己被綠了蔓同。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡胡本,死狀恐怖牌柄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情侧甫,我是刑警寧澤珊佣,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站披粟,受9級特大地震影響咒锻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜守屉,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一惑艇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拇泛,春花似錦滨巴、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至熄守,卻和暖如春蜈垮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背裕照。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工攒发, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人晋南。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓惠猿,卻偏偏與公主長得像,于是被迫代替她去往敵國和親负间。 傳聞我的和親對象是個殘疾皇子偶妖,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

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

  • Java8張圖 11、字符串不變性 12唉擂、equals()方法、hashCode()方法的區(qū)別 13檀葛、...
    Miley_MOJIE閱讀 3,707評論 0 11
  • Java多線程學習 [-] 一擴展javalangThread類 二實現(xiàn)javalangRunnable接口 三T...
    影馳閱讀 2,959評論 1 18
  • 線程安全 當多個線程訪問一個對象時玩祟,如果不用考慮這些線程在運行時環(huán)境下的調(diào)度和交替執(zhí)行,也不需要進行額外的同步屿聋,或...
    閩越布衣閱讀 767評論 0 6
  • 剛剛?cè)肭锏脑绯刻鞖馕隹赵衲晡乙布尤氤烤毜男辛胁厝担飞嫌心_步匆匆快走的~也有三兩個聊著天走走停停的~還有跳舞的...
    欣賞66閱讀 348評論 3 4
  • 今天我們1405班聚會,我們對1405有很深的感情转锈,在照相的時候都喊的是1405盘寡。大家都非常珍惜來之不易的見面機會...
    草莓牛奶咖啡布丁閱讀 61評論 0 0