真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

悲觀鎖java代碼實現(xiàn) 悲觀鎖 java

Java鎖有哪些種類,以及區(qū)別

一、公平鎖/非公平鎖

保定ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場景,ssl證書未來市場廣闊!成為創(chuàng)新互聯(lián)公司的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:13518219792(備注:SSL證書合作)期待與您的合作!

公平鎖是指多個線程按照申請鎖的順序來獲取鎖。

非公平鎖是指多個線程獲取鎖的順序并不是按照申請鎖的順序,有可能后申請的線程比先申請的線程優(yōu)先獲取鎖。有可能,會造成優(yōu)先級反轉(zhuǎn)或者饑餓現(xiàn)象。

對于Java ReentrantLock而言,通過構(gòu)造函數(shù)指定該鎖是否是公平鎖,默認(rèn)是非公平鎖。非公平鎖的優(yōu)點在于吞吐量比公平鎖大。

對于Synchronized而言,也是一種非公平鎖。由于其并不像ReentrantLock是通過AQS的來實現(xiàn)線程調(diào)度,所以并沒有任何辦法使其變成公平鎖。

二、可重入鎖

可重入鎖又名遞歸鎖,是指在同一個線程在外層方法獲取鎖的時候,在進(jìn)入內(nèi)層方法會自動獲取鎖。說的有點抽象,下面會有一個代碼的示例。

對于Java ReentrantLock而言, 他的名字就可以看出是一個可重入鎖,其名字是Re entrant Lock重新進(jìn)入鎖。

對于Synchronized而言,也是一個可重入鎖。可重入鎖的一個好處是可一定程度避免死鎖。

synchronized void setA() throws Exception{

Thread.sleep(1000);

setB();

}

synchronized void setB() throws Exception{

Thread.sleep(1000);

}

上面的代碼就是一個可重入鎖的一個特點,如果不是可重入鎖的話,setB可能不會被當(dāng)前線程執(zhí)行,可能造成死鎖。

三、獨享鎖/共享鎖

獨享鎖是指該鎖一次只能被一個線程所持有。

共享鎖是指該鎖可被多個線程所持有。

對于Java

ReentrantLock而言,其是獨享鎖。但是對于Lock的另一個實現(xiàn)類ReadWriteLock,其讀鎖是共享鎖,其寫鎖是獨享鎖。

讀鎖的共享鎖可保證并發(fā)讀是非常高效的,讀寫,寫讀 ,寫寫的過程是互斥的。

獨享鎖與共享鎖也是通過AQS來實現(xiàn)的,通過實現(xiàn)不同的方法,來實現(xiàn)獨享或者共享。

對于Synchronized而言,當(dāng)然是獨享鎖。

四、互斥鎖/讀寫鎖

上面講的獨享鎖/共享鎖就是一種廣義的說法,互斥鎖/讀寫鎖就是具體的實現(xiàn)。

互斥鎖在Java中的具體實現(xiàn)就是ReentrantLock

讀寫鎖在Java中的具體實現(xiàn)就是ReadWriteLock

五、樂觀鎖/悲觀鎖

樂觀鎖與悲觀鎖不是指具體的什么類型的鎖,而是指看待并發(fā)同步的角度。

悲觀鎖認(rèn)為對于同一個數(shù)據(jù)的并發(fā)操作,一定是會發(fā)生修改的,哪怕沒有修改,也會認(rèn)為修改。因此對于同一個數(shù)據(jù)的并發(fā)操作,悲觀鎖采取加鎖的形式。悲觀的認(rèn)為,不加鎖的并發(fā)操作一定會出問題。

樂觀鎖則認(rèn)為對于同一個數(shù)據(jù)的并發(fā)操作,是不會發(fā)生修改的。在更新數(shù)據(jù)的時候,會采用嘗試更新,不斷重新的方式更新數(shù)據(jù)。樂觀的認(rèn)為,不加鎖的并發(fā)操作是沒有事情的。

從上面的描述我們可以看出,悲觀鎖適合寫操作非常多的場景,樂觀鎖適合讀操作非常多的場景,不加鎖會帶來大量的性能提升。

悲觀鎖在Java中的使用,就是利用各種鎖。

樂觀鎖在Java中的使用,是無鎖編程,常常采用的是CAS算法,典型的例子就是原子類,通過CAS自旋實現(xiàn)原子操作的更新。

六、分段鎖

分段鎖其實是一種鎖的設(shè)計,并不是具體的一種鎖,對于ConcurrentHashMap而言,其并發(fā)的實現(xiàn)就是通過分段鎖的形式來實現(xiàn)高效的并發(fā)操作。

我們以ConcurrentHashMap來說一下分段鎖的含義以及設(shè)計思想,ConcurrentHashMap中的分段鎖稱為Segment,它即類似于HashMap(JDK7與JDK8中HashMap的實現(xiàn))的結(jié)構(gòu),即內(nèi)部擁有一個Entry數(shù)組,數(shù)組中的每個元素又是一個鏈表;同時又是一個ReentrantLock(Segment繼承了ReentrantLock)。

當(dāng)需要put元素的時候,并不是對整個hashmap進(jìn)行加鎖,而是先通過hashcode來知道他要放在那一個分段中,然后對這個分段進(jìn)行加鎖,所以當(dāng)多線程put的時候,只要不是放在一個分段中,就實現(xiàn)了真正的并行的插入。

但是,在統(tǒng)計size的時候,可就是獲取hashmap全局信息的時候,就需要獲取所有的分段鎖才能統(tǒng)計。

分段鎖的設(shè)計目的是細(xì)化鎖的粒度,當(dāng)操作不需要更新整個數(shù)組的時候,就僅僅針對數(shù)組中的一項進(jìn)行加鎖操作。

七、偏向鎖/輕量級鎖/重量級鎖

這三種鎖是指鎖的狀態(tài),并且是針對Synchronized。在Java

5通過引入鎖升級的機制來實現(xiàn)高效Synchronized。這三種鎖的狀態(tài)是通過對象監(jiān)視器在對象頭中的字段來表明的。

偏向鎖是指一段同步代碼一直被一個線程所訪問,那么該線程會自動獲取鎖。降低獲取鎖的代價。

輕量級鎖是指當(dāng)鎖是偏向鎖的時候,被另一個線程所訪問,偏向鎖就會升級為輕量級鎖,其他線程會通過自旋的形式嘗試獲取鎖,不會阻塞,提高性能。

重量級鎖是指當(dāng)鎖為輕量級鎖的時候,另一個線程雖然是自旋,但自旋不會一直持續(xù)下去,當(dāng)自旋一定次數(shù)的時候,還沒有獲取到鎖,就會進(jìn)入阻塞,該鎖膨脹為重量級鎖。重量級鎖會讓其他申請的線程進(jìn)入阻塞,性能降低。

八、自旋鎖

在Java中,自旋鎖是指嘗試獲取鎖的線程不會立即阻塞,而是采用循環(huán)的方式去嘗試獲取鎖,這樣的好處是減少線程上下文切換的消耗,缺點是循環(huán)會消耗CPU。

典型的自旋鎖實現(xiàn)的例子,可以參考自旋鎖的實現(xiàn)

java中悲觀鎖和樂觀鎖的區(qū)別

樂觀鎖和悲觀鎖的區(qū)別如下:

1、悲觀鎖是當(dāng)線程拿到資源時,就對資源上鎖,并在提交后,才釋放鎖資源,其他線程才能使用資源。

2、樂觀鎖是當(dāng)線程拿到資源時,上樂觀鎖,在提交之前,其他的鎖也可以操作這個資源,當(dāng)有沖突的時候,并發(fā)機制會保留前一個提交,打回后一個提交,讓后一個線程重新獲取資源后,再操作,然后提交。和git上傳代碼一樣,兩個線程都不是直接獲取資源本身,而是先獲取資源的兩個copy版本,然后在這兩個copy版本上修改。

3、悲觀鎖和樂觀鎖在并發(fā)量低的時候,性能差不多,但是在并發(fā)量高的時候,樂觀鎖的性能遠(yuǎn)遠(yuǎn)優(yōu)于悲觀鎖。

4、常用的synchronized是悲觀鎖,lock是樂觀鎖。

請問一個悲觀鎖的問題,就是在程序中實現(xiàn)該如何做啊,謝謝

假如要按照你這種 悲觀鎖 的處理機制的話, 那么

Connection conn 要定義在外面, 也就是類這個級別。

例如

public class Test {

Connection conn;

public Product get(id) {

conn = getConn();

conn.startTraction();

...............select * from product where id = 10 for update;

}

public void update(long id) {

// 這個時候, 用類一個級別的 Conn , 不用新建

update product set name = 'xxx' where id = :id;

// 更新完畢后

Commit 事務(wù)。

關(guān)閉 Conn.

}

}

上面的只是 原理的 例子, 實際情況下,不推薦這么寫

因為 如果有人 只 Product get(id) 不 update(long id) 的話, 事情就麻煩了。

Java如何實現(xiàn)對Mysql數(shù)據(jù)庫的行鎖

下面通過一個例子來說明

場景如下:

用戶賬戶有余額,當(dāng)發(fā)生交易時,需要實時更新余額。這里如果發(fā)生并發(fā)問題,那么會造成用戶余額和實際交易的不一致,這對公司和客戶來說都是很危險的。

那么如何避免:

網(wǎng)上查了下,有以下兩種方法:

1、使用悲觀鎖

當(dāng)需要變更余額時,通過代碼在事務(wù)中對當(dāng)前需要更新的記錄設(shè)置for update行鎖,然后開始正常的查詢和更新操作

這樣,其他的事務(wù)只能等待該事務(wù)完成后方可操作

當(dāng)然要特別注意,如果使用了Spring的事務(wù)注解,需要配置一下:

!-- (事務(wù)管理)transaction manager, use JtaTransactionManager for global tx --

bean id="transactionManager"

class="org.springframework.jdbc.datasource.DataSourceTransactionManager"

property name="dataSource" ref="dataSource" /

/bean

!-- 使用annotation定義事務(wù) --

tx:annotation-driven transaction-manager="transactionManager" /

在指定代碼處添加事務(wù)注解

@Transactional

@Override

public boolean increaseBalanceByLock(Long userId, BigDecimal amount)

throws ValidateException {

long time = System.currentTimeMillis();

//獲取對記錄的鎖定

UserBalance balance = userBalanceDao.getLock(userId);

LOGGER.info("[lock] start. time: {}", time);

if (null == balance) {

throw new ValidateException(

ValidateErrorCode.ERRORCODE_BALANCE_NOTEXIST,

"user balance is not exist");

}

boolean result = userBalanceDao.increaseBalanceByLock(balance, amount);

long timeEnd = System.currentTimeMillis();

LOGGER.info("[lock] end. time: {}", timeEnd);

return result;

}

MyBatis中的鎖定方式,實際測試該方法確實可以有效控制,不過在大并發(fā)量的情況下,可能會有性能問題吧

select id="getLock" resultMap="BaseResultMap" parameterType="java.lang.Long"

![CDATA[

select * from user_balance where id=#{id,jdbcType=BIGINT} for update;

]]

/select

2、使用樂觀鎖

這個方法也同樣可以解決場景中描述的問題(我認(rèn)為比較適合并不頻繁的操作):

設(shè)計表的時候增加一個version(版本控制字段),每次需要更新余額的時候,先獲取對象,update的時候根據(jù)version和id為條件去更新,如果更新回來的數(shù)量為0,說明version已經(jīng)變更

需要重復(fù)一次更新操作,如下:sql腳本

update user_balance set Balance = #{balance,jdbcType=DECIMAL},Version = Version+1 where Id = #{id,jdbcType=BIGINT} and Version = #{version,jdbcType=BIGINT}

這是一種不使用數(shù)據(jù)庫鎖的方法,解決方式也很巧妙。當(dāng)然,在大量并發(fā)的情況下,一次扣款需要重復(fù)多次的操作才能成功,還是有不足之處的。不知道還有沒有更好的方法。


標(biāo)題名稱:悲觀鎖java代碼實現(xiàn) 悲觀鎖 java
網(wǎng)址分享:http://weahome.cn/article/hehdoo.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部