Java并發(fā)編程的悲觀鎖和樂觀鎖機(jī)制

本篇內(nèi)容主要講解“Java并發(fā)編程的悲觀鎖和樂觀鎖機(jī)制”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“Java并發(fā)編程的悲觀鎖和樂觀鎖機(jī)制”吧!

創(chuàng)新互聯(lián)堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:成都網(wǎng)站建設(shè)、網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時(shí)代的紅橋網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!

一、資源和加鎖

1、場(chǎng)景描述

多線程并發(fā)訪問同一個(gè)資源問題,假如線程A獲取變量之后修改變量值,線程C在此時(shí)也獲取變量值并且修改,兩個(gè)線程同時(shí)并發(fā)處理一個(gè)變量,就會(huì)導(dǎo)致并發(fā)問題。

Java并發(fā)編程的悲觀鎖和樂觀鎖機(jī)制

這種并行處理數(shù)據(jù)庫的情況在實(shí)際的業(yè)務(wù)開發(fā)中很常見,兩個(gè)線程先后修改數(shù)據(jù)庫的值,導(dǎo)致數(shù)據(jù)有問題,該問題復(fù)現(xiàn)的概率不大,處理的時(shí)候需要對(duì)整個(gè)模塊體系有概念,才能容易定位問題。

2、演示案例

public class LockThread01 {     public static void main(String[] args) {         CountAdd countAdd = new CountAdd() ;         AddThread01 addThread01 = new AddThread01(countAdd) ;         addThread01.start();         AddThread02 varThread02 = new AddThread02(countAdd) ;         varThread02.start();     } } class AddThread01 extends Thread {     private CountAdd countAdd  ;     public AddThread01 (CountAdd countAdd){         this.countAdd = countAdd ;     }     @Override     public void run() {         countAdd.countAdd(30);     } } class AddThread02 extends Thread {     private CountAdd countAdd  ;     public AddThread02 (CountAdd countAdd){         this.countAdd = countAdd ;     }     @Override     public void run() {         countAdd.countAdd(10);     } } class CountAdd {     private Integer count = 0 ;     public void countAdd (Integer num){         try {             if (num == 30){                 count = count + 50 ;                 Thread.sleep(3000);             } else {                 count = count + num ;             }             System.out.println("num="+num+";count="+count);         } catch (Exception e){             e.printStackTrace();         }     } }

這里案例演示多線程并發(fā)修改count值,導(dǎo)致和預(yù)期不一致的結(jié)果,這是多線程并發(fā)下最常見的問題,尤其是在并發(fā)更新數(shù)據(jù)時(shí)。

出現(xiàn)并發(fā)的情況時(shí),就需要通過一定的方式或策略來控制在并發(fā)情況下數(shù)據(jù)讀寫的準(zhǔn)確性,這被稱為并發(fā)控制,實(shí)現(xiàn)并發(fā)控制手段也很多,最常見的方式是資源加鎖,還有一種簡(jiǎn)單的實(shí)現(xiàn)策略:修改數(shù)據(jù)前讀取數(shù)據(jù),修改的時(shí)候加入限制條件,保證修改的內(nèi)容在此期間沒有被修改。

二、鎖的概念簡(jiǎn)介

1、鎖機(jī)制簡(jiǎn)介

并發(fā)編程中一個(gè)最關(guān)鍵的問題,多線程并發(fā)處理同一個(gè)資源,防止資源使用的沖突一個(gè)關(guān)鍵解決方法,就是在資源上加鎖:多線程序列化訪問。鎖是用來控制多個(gè)線程訪問共享資源的方式,鎖機(jī)制能夠讓共享資源在任意給定時(shí)刻只有一個(gè)線程任務(wù)訪問,實(shí)現(xiàn)線程任務(wù)的同步互斥,這是最理想但性能最差的方式,共享讀鎖的機(jī)制允許多任務(wù)并發(fā)訪問資源。

2、悲觀鎖

悲觀鎖,總是假設(shè)每次每次被讀取的數(shù)據(jù)會(huì)被修改,所以要給讀取的數(shù)據(jù)加鎖,具有強(qiáng)烈的資源獨(dú)占和排他特性,在整個(gè)數(shù)據(jù)處理過程中,將數(shù)據(jù)處于鎖定狀態(tài),例如synchronized關(guān)鍵字的實(shí)現(xiàn)就是悲觀機(jī)制。

Java并發(fā)編程的悲觀鎖和樂觀鎖機(jī)制

悲觀鎖的實(shí)現(xiàn),往往依靠數(shù)據(jù)庫提供的鎖機(jī)制,只有數(shù)據(jù)庫層提供的鎖機(jī)制才能真正保證數(shù)據(jù)訪問的排他性,否則,即使在本系統(tǒng)中實(shí)現(xiàn)了加鎖機(jī)制,也無法保證外部系統(tǒng)不會(huì)修改數(shù)據(jù),悲觀鎖主要分為共享讀鎖和排他寫鎖。

排他鎖基本機(jī)制:又稱寫鎖,允許獲取排他鎖的事務(wù)更新數(shù)據(jù),阻止其他事務(wù)取得相同的資源的共享讀鎖和排他鎖。若事務(wù)T對(duì)數(shù)據(jù)對(duì)象A加上寫鎖,事務(wù)T可以讀A也可以修改A,其他事務(wù)不能再對(duì)A加任何鎖,直到T釋放A上的寫鎖。

3、樂觀鎖

樂觀鎖相對(duì)悲觀鎖而言,采用更加寬松的加鎖機(jī)制。悲觀鎖大多數(shù)情況下依靠數(shù)據(jù)庫的鎖機(jī)制實(shí)現(xiàn),以保證操作最大程度的獨(dú)占性。但隨之而來的就是數(shù)據(jù)庫性能的大量開銷,特別是對(duì)長(zhǎng)事務(wù)的開銷非常的占資源,樂觀鎖機(jī)制在一定程度上解決了這個(gè)問題。

Java并發(fā)編程的悲觀鎖和樂觀鎖機(jī)制

樂觀鎖大多是基于數(shù)據(jù)版本記錄機(jī)制實(shí)現(xiàn),為數(shù)據(jù)增加一個(gè)版本標(biāo)識(shí),在基于數(shù)據(jù)庫表的版本解決方案中,一般是通過為數(shù)據(jù)庫表增加一個(gè)version字段來實(shí)現(xiàn)。讀取出數(shù)據(jù)時(shí),將此版本號(hào)一同讀出,之后更新時(shí),對(duì)此版本號(hào)加一。此時(shí),將提交數(shù)據(jù)的版本數(shù)據(jù)與數(shù)據(jù)庫表對(duì)應(yīng)記錄的當(dāng)前版本信息進(jìn)行比對(duì),如果提交的數(shù)據(jù)版本號(hào)等于數(shù)據(jù)庫表當(dāng)前版本號(hào),則予以更新,否則認(rèn)為是過期數(shù)據(jù)。樂觀鎖機(jī)制在高并發(fā)場(chǎng)景下,可能會(huì)導(dǎo)致大量更新失敗的操作。

樂觀鎖的實(shí)現(xiàn)是策略層面的實(shí)現(xiàn):CAS(Compare-And-Swap)。當(dāng)多個(gè)線程嘗試使用CAS同時(shí)更新同一個(gè)變量時(shí),只有其中一個(gè)線程能成功更新變量的值,而其它線程都失敗,失敗的線程并不會(huì)被掛起,而是被告知這次競(jìng)爭(zhēng)中失敗,并可以再次嘗試。

4、機(jī)制對(duì)比

悲觀鎖本身的實(shí)現(xiàn)機(jī)制就以損失性能為代價(jià),多線程爭(zhēng)搶,加鎖、釋放鎖會(huì)導(dǎo)致比較多的上下文切換和調(diào)度延時(shí),加鎖的機(jī)制會(huì)產(chǎn)生額外的開銷,還有增加產(chǎn)生死鎖的概率,引發(fā)性能問題。

樂觀鎖雖然會(huì)基于對(duì)比檢測(cè)的手段判斷更新的數(shù)據(jù)是否有變化,但是不確定數(shù)據(jù)是否變化完成,例如線程1讀取的數(shù)據(jù)是A1,但是線程2操作A1的值變化為A2,然后再次變化為A1,這樣線程1的任務(wù)是沒有感知的。

悲觀鎖每一次數(shù)據(jù)修改都要上鎖,效率低,寫數(shù)據(jù)失敗的概率比較低,比較適合用在寫多讀少場(chǎng)景。

樂觀鎖并未真正加鎖,效率高,寫數(shù)據(jù)失敗的概率比較高,容易發(fā)生業(yè)務(wù)形異常,比較適合用在讀多寫少場(chǎng)景。

是選擇犧牲性能,還是追求效率,要根據(jù)業(yè)務(wù)場(chǎng)景判斷,這種選擇需要依賴經(jīng)驗(yàn)判斷,不過隨著技術(shù)迭代,數(shù)據(jù)庫的效率提升,集群模式的出現(xiàn),性能和效率還是可以兩全的。

三、Lock基礎(chǔ)案例

1、Lock方法說明

lock:執(zhí)行一次獲取鎖,獲取后立即返回;

lockInterruptibly:在獲取鎖的過程中可以中斷;

tryLock:嘗試非阻塞獲取鎖,可以設(shè)置超時(shí)時(shí)間,如果獲取成功返回true,有利于線程的狀態(tài)監(jiān)控;

unlock:釋放鎖,清理線程狀態(tài);

newCondition:獲取等待通知組件,和當(dāng)前鎖綁定;

2、應(yīng)用案例

import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockThread02 {     public static void main(String[] args) {         LockNum lockNum = new LockNum() ;         LockThread lockThread1 = new LockThread(lockNum,"TH1");         LockThread lockThread2 = new LockThread(lockNum,"TH2");         LockThread lockThread3 = new LockThread(lockNum,"TH3");         lockThread1.start();         lockThread2.start();         lockThread3.start();     } } class LockNum {     private Lock lock = new ReentrantLock() ;     public void getNum (){         lock.lock();         try {             for (int i = 0 ; i < 3 ; i++){                 System.out.println("ThreadName:"+Thread.currentThread().getName()+";i="+i);             }         } finally {             lock.unlock();         }     } } class LockThread extends Thread {     private LockNum lockNum ;     public LockThread (LockNum lockNum,String name){         this.lockNum = lockNum ;         super.setName(name);     }     @Override     public void run() {         lockNum.getNum();     } }

這里多線程基于Lock鎖機(jī)制,分別依次執(zhí)行任務(wù),這是Lock的基礎(chǔ)用法,各種API的詳解,下次再說。

3、與synchronized對(duì)比

基于synchronized實(shí)現(xiàn)的鎖機(jī)制,安全性很高,但是一旦線程失敗,直接拋出異常,沒有清理線程狀態(tài)的機(jī)會(huì)。顯式的使用Lock語法,可以在finally語句中最終釋放鎖,維護(hù)相對(duì)正常的線程狀態(tài),在獲取鎖的過程中,可以嘗試獲取,或者嘗試獲取鎖一段時(shí)間。

到此,相信大家對(duì)“Java并發(fā)編程的悲觀鎖和樂觀鎖機(jī)制”有了更深的了解,不妨來實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

名稱欄目:Java并發(fā)編程的悲觀鎖和樂觀鎖機(jī)制
網(wǎng)頁網(wǎng)址:http://muchs.cn/article6/iegpig.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站設(shè)計(jì)、域名注冊(cè)外貿(mào)建站、網(wǎng)頁設(shè)計(jì)公司網(wǎng)站策劃、云服務(wù)器

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)頁設(shè)計(jì)公司