1.凡使用synchronized標記的方法,比如 public synchronized void func1() { .... },則同時只有一個線程能夠運行這個方法。比如,線程1正在運行func1,則其他線程需要運行func1的話,會卡住,等線程1運行func1結(jié)束后,其他線程中,才會有一個幸運兒成功爭取到運行func1的資格,然后這個幸運兒線程開始運行func1。沒有爭取到運行資格的其他線程,會繼續(xù)等待。
孟州網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)!從網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、響應(yīng)式網(wǎng)站等網(wǎng)站項目制作,到程序開發(fā),運營維護。創(chuàng)新互聯(lián)成立于2013年到現(xiàn)在10年的時間,我們擁有了豐富的建站經(jīng)驗和運維經(jīng)驗,來保證我們的工作的順利進行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)。
2.你的例子中,被鎖定的是 方法 m1,而不是屬性b。所以,m1的synchronized加鎖操作,與b沒有半點毛錢關(guān)系。
3.要實現(xiàn)你的鎖b想法,其實很簡單。去買一件貞操寶甲來就行了。開玩笑,哈哈。要鎖b,把main方法里的tt.m2()修改為tt.m1()。
4.以后別用“b”作為變量,總覺得怪怪了。也許你現(xiàn)在還沒長大,很單純。但大人的世界里,“b”是一種不文雅但又對人類的未來有重要作用的東西。建議用cb來代替b。
與java無關(guān),要用sql語句實現(xiàn)
前提目標表要有索引,查詢要開啟事物,使用select * from tb with(updlock) where col = xxx將一行數(shù)據(jù)鎖住,其他連接不能再修改表
對象是一個鎖標志。按照先到先得的原則,如果有多個線程都會執(zhí)行代碼,并使用同一個對象作為鎖,
synchronize(對象){ .... }
那么,先執(zhí)行這段代碼的那個線程,將會獲得這個對象鎖,而當這個線程執(zhí)行這段代碼的時候,其他線程也是使用這個對象作為鎖的,就不能執(zhí)行這段代碼,知道最初得到這個鎖的線程運行完這段代碼,然后再把鎖分配給下一個線程執(zhí)行。
下面通過一個例子來說明
場景如下:
用戶賬戶有余額,當發(fā)生交易時,需要實時更新余額。這里如果發(fā)生并發(fā)問題,那么會造成用戶余額和實際交易的不一致,這對公司和客戶來說都是很危險的。
那么如何避免:
網(wǎng)上查了下,有以下兩種方法:
1、使用悲觀鎖
當需要變更余額時,通過代碼在事務(wù)中對當前需要更新的記錄設(shè)置for update行鎖,然后開始正常的查詢和更新操作
這樣,其他的事務(wù)只能等待該事務(wù)完成后方可操作
當然要特別注意,如果使用了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、使用樂觀鎖
這個方法也同樣可以解決場景中描述的問題(我認為比較適合并不頻繁的操作):
設(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ù)庫鎖的方法,解決方式也很巧妙。當然,在大量并發(fā)的情況下,一次扣款需要重復(fù)多次的操作才能成功,還是有不足之處的。不知道還有沒有更好的方法。
public Object getObject(String key,Object o) {
synchronized (map) {
if(map.get(key)==null) {
map.put(key,o)
}else {
return map.get(key);
}}// 格式?jīng)]法弄,自己弄一下
}
// demol0326 的回答意思使用MyTest的實例來加鎖,但是map是static的,無法鎖住
// MyTest的多個實例在多個線程中的請求
//禾木雙子 :如果A線程在getObject方法的第二行停止,此時B線程進入getObject后執(zhí)行部分代碼, 此時B線程停止,A線程啟動,他不會執(zhí)行'多線程處理', 仍然有線程線程安全問題,(想像一下更多的線程進入該方法的情況,num甚至會得到負值)