java線程同步原理
java會為每個object對象分配一個monitor,當某個對象的同步方法(synchronizedmethods)被多個線程調(diào)用時,該對象的monitor將負責處理這些訪問的并發(fā)獨占要求。
當一個線程調(diào)用一個對象的同步方法時,JVM會檢查該對象的monitor。如果monitor沒有被占用,那么這個線程就得到了monitor的占有權(quán),可以繼續(xù)執(zhí)行該對象的同步方法;如果monitor被其他線程所占用,那么該線程將被掛起,直到monitor被釋放。
當線程退出同步方法調(diào)用時,該線程會釋放monitor,這將允許其他等待的線程獲得monitor以使對同步方法的調(diào)用執(zhí)行下去。
注意:Java對象的monitor機制和傳統(tǒng)的臨界檢查代碼區(qū)技術(shù)不一樣。java的一個同步方法并不意味著同時只有一個線程獨占執(zhí)行,但臨界檢查代碼區(qū)技術(shù)確實會保證同步方法在一個時刻只被一個線程獨占執(zhí)行。Java的monitor機制的準確含義是:任何時刻,對一個指定object對象的某同步方法只能由一個線程來調(diào)用。
java對象的monitor是跟隨object實例來使用的,而不是跟隨程序代碼。兩個線程可以同時執(zhí)行相同的同步方法,比如:一個類的同步方法是xMethod(),有a,b兩個對象實例,一個線程執(zhí)行a.xMethod(),另一個線程執(zhí)行b.xMethod().互不沖突。
Lock是java5提供的一個強大的線程同步機制--通過顯示定義同步鎖對象來實現(xiàn)同步。Lock可以顯示的加鎖、解鎖。每次只能有一個線程對lock對象加鎖。
Lock有ReadLock、WriteLock、ReentrantLock(可重入鎖)
常用的就是ReentrantLock。代碼如下:
代碼邏輯:Account賬戶類,實現(xiàn)取錢的同步方法、DrawThread取錢的線程
Account:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
package lock.reentrantlock2; import java.util.concurrent.locks.*; /** *賬戶類,需保持同步 */ public class Account { //定義鎖對象 private final ReentrantLock lock = new ReentrantLock(); private String accountNo; private double balance; public Account(){} public Account(String accountNo , double balance) { this .accountNo = accountNo; this .balance = balance; } public void setAccountNo(String accountNo) { this .accountNo = accountNo; } public String getAccountNo() { return this .accountNo; } public double getBalance() { return this .balance; } public void draw( double drawAmount) { lock.lock(); try { //賬戶余額大于取錢數(shù)目 if (balance >= drawAmount) { //吐出鈔票 System.out.println(Thread.currentThread().getName() + "取錢成功!吐出鈔票:" + drawAmount); try { Thread.sleep( 1 ); } catch (InterruptedException ex) { ex.printStackTrace(); } //修改余額 balance -= drawAmount; System.out.println( "\t余額為: " + balance); } else { System.out.println(Thread.currentThread().getName() + "取錢失敗!余額不足!" ); } } finally { lock.unlock(); } } public int hashCode() { return accountNo.hashCode(); } public boolean equals(Object obj) { if (obj != null && obj.getClass() == Account. class ) { Account target = (Account)obj; return target.getAccountNo().equals(accountNo); } return false ; } } |
DrawThread:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package lock.reentrantlock2; /** * 調(diào)用account取錢 * */ public class DrawThread extends Thread { //模擬用戶賬戶 private Account account; //當前取錢線程所希望取的錢數(shù) private double drawAmount; public DrawThread(String name , Account account , double drawAmount) { super (name); this .account = account; this .drawAmount = drawAmount; } //當多條線程修改同一個共享數(shù)據(jù)時,將涉及到數(shù)據(jù)安全問題。 public void run() { account.draw(drawAmount); } } |
TestDraw:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package lock.reentrantlock2; /** */ public class TestDraw { public static void main(String[] args) { //創(chuàng)建一個賬戶 Account acct = new Account( "1234567" , 1000 ); //模擬兩個線程對同一個賬戶取錢 new DrawThread( "甲" , acct , 800 ).start(); new DrawThread( "乙" , acct , 800 ).start(); } } |
運行結(jié)果:
甲取錢成功!吐出鈔票:800.0
余額為:200.0
乙取錢失敗!余額不足!
使用Lock同步與同步方法很相似,都是“加鎖--修改公共變量--釋放鎖”的模式,代碼很容易看懂。兩個線程對應(yīng)一個Account對象,保證了兩個線程對應(yīng)一個lock對象,保證了同一時刻只有一個線程進入臨界區(qū)。Lock還包含太容易Lock(),以及試圖獲取可中斷鎖的lockInterruptibly(),獲取超時失效鎖的tryLock(long,TimeUnit)等方法。
ReentrantLock鎖具有可重入性可以對已被加鎖的ReentrantLock鎖再次加鎖,線程每次調(diào)用lock()加鎖后,必須顯示的調(diào)用unlock來釋放鎖,有幾個lock就對應(yīng)幾個unlock。還有把unlock放在finally代碼塊中,Lock在發(fā)生異常時也是不釋放鎖的,所以在finally中釋放更安全。
總結(jié)
以上就是本文關(guān)于Java線程同步Lock同步鎖代碼示例的全部內(nèi)容,希望對大家有所幫助。有什么問題可以隨時留言,小編會及時回復(fù)大家的。感謝朋友們對本站的支持!
原文鏈接:http://www.cnblogs.com/jycboy/p/5623113.html