外贸网站建站m/优化设计答案六年级
在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类。介绍的内容如下:5 E- M/ l& @* f! f
1.公平锁 / 非公平锁1 q# A% B1 u. M; ]1 w/ C" @
2.可重入锁 / 不可重入锁: t3 C+ J) L8 g, H
3.独享锁 / 共享锁
7 `+ z. n/ y8 [3 O6 ]3 N4.互斥锁 / 读写锁/ `# L) x4 h* V& |0 k
5.乐观锁 / 悲观锁
9 f& D% {! `& E' Y6 z6.分段锁( X. o ?+ ` [ V* E ^
7.偏向锁 / 轻量级锁 / 重量级锁
' B) F2 d9 {/ y6 F* r) W) y8.自旋锁
Q) |) B" e) c1 c上面是很多锁的名词,这些分类并不是全是指锁的状态,有的指锁的特性,有的指锁的设计,下面总结的内容是对每个锁的名词进行一定的解释。: \& Y1 g& U. l# `7 |
公平锁 / 非公平锁. l" Q; b6 p3 n$ J. u- H: Y
公平锁
; K! C# X" O* m& S V8 \, W, Y公平锁是指多个线程按照申请锁的顺序来获取锁。
& W+ [, H" C$ \5 a' C2 y. Y非公平锁0 P( m6 V& c! h8 I/ R, c6 m4 u
非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。
# p, U9 U* O. Q$ U+ i对于Java ReentrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁。非公平锁的优点在于吞吐量比公平锁大。
& Z5 R. r- m$ R, y- ]% u对于7 k; u6 P2 E1 g; t7 J- N9 C% q
Synchronized而言,也是一种非公平锁。由于其并不像ReentrantLock是通过AQS的来实现线程调度,所以并没有任何办法使其变成公平锁。
% L% R1 r/ R* d8 j6 M5 j F1 r可重入锁 / 不可重入锁: H6 V: {0 H7 g0 {5 }0 B* n5 } b! |
可重入锁) ~5 d; w1 }$ q& F7 e
广义上的可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁。ReentrantLock和synchronized都是可重入锁3 {8 Q" i0 ]5 r7 l+ i: Z7 }
synchronizedvoidsetA()throwsException{
. M) C# \& ?3 t' M( UThread.sleep(1000);
" Z( d0 q/ S: k: B" YsetB();
: k- T8 U4 _0 @4 t}: v6 ~6 U/ o/ e2 K
synchronizedvoidsetB()throwsException{6 h4 J2 p) ]) R8 Q0 h+ s! s
Thread.sleep(1000);. V9 r, F5 U( Z4 o; k
}
' g9 Z& g3 I C7 g, U上面的代码就是一个可重入锁的一个特点,如果不是可重入锁的话,setB可能不会被当前线程执行,可能造成死锁。
1 V, I# q% ?) B9 N/ r8 f不可重入锁# {8 @% A* p& |8 D8 G* T3 g* ~
不可重入锁,与可重入锁相反,不可递归调用,递归调用就发生死锁。看到一个经典的讲解,使用自旋锁来模拟一个不可重入锁,代码如下
& M# U* `( p( Fimportjava.util.concurrent.atomic.AtomicReference;
q6 Z- H& O3 `" H# EpublicclassUnreentrantLock{
6 h- ]# t' [) Z5 F0 n0 I |privateAtomicReference owner =newAtomicReference();
g. q9 v1 x4 |, p% Lpublicvoidlock(){
* G M0 a& m0 SThread current = Thread.currentThread();& d1 f0 d0 X# O, f
//这句是很经典的“自旋”语法,AtomicInteger中也有
0 t6 d4 I9 \2 W* Wfor(;;) {
4 E$ q1 p. y: N3 K2 d. [if(!owner.compareAndSet(null, current)) {5 y+ p: G4 @6 [% `- U6 g! w2 s( G
return;
& `- I p8 ? y8 s& G7 A0 |}9 V& H7 P* q# r
}
: h# |3 k3 R3 y}8 R" ~3 \# q; Q
publicvoidunlock(){8 x, T& ~2 r' s4 V; {4 I" E" ?" _" ]
Thread current = Thread.currentThread();2 Y; K- F2 \) o3 m: x
owner.compareAndSet(current,null);
7 ^$ L& ^, t- w l}
. a7 U$ }, }; o; Q}5 W" h/ S# Q8 z2 W4 M) Z- O; ]/ }. W; b
代码也比较简单,使用原子引用来存放线程,同一线程两次调用lock()方法,如果不执行unlock()释放锁的话,第二次调用自旋的时候就会产生死锁,这个锁就不是可重入的,而实际上同一个线程不必每次都去释放锁再来获取锁,这样的调度切换是很耗资源的。5 z; |1 q6 E+ T) c. Q: f
把它变成一个可重入锁:/ n# _1 Q* N& k) h4 q
import java.util.concurrent.atomic.AtomicReference;
- i) m9 g* Y# Q. q7 g; Y1 LpublicclassUnreentrantLock{$ R/ F2 b* O% r2 i5 h! `6 v( K) R3 ]7 j
privateAtomicReference owner =newAtomicReference();
+ s n* F/ B: |. |( n) V6 Bprivateintstate =0;$ b1 r$ K! f7 J$ [2 U
publicvoidlock(){' K! ?7 T9 ]* {' \' ^
Thread current = Thread.currentThread();4 S- v7 x R3 s4 ?2 | C ]
if(current == owner.get()) {
" d2 B1 ]! n/ @ dstate++;
B1 k0 L! S- Wreturn;# b5 P( k1 D$ P2 ~
}1 t9 c2 i; F; X) f
//这句是很经典的“自旋”式语法,AtomicInteger中也有
6 w" r5 }( q, E- T+ D& `for(;;) {; I: f5 h& s' E, I
if(!owner.compareAndSet(null, current)) {6 ^2 u1 `1 Y+ h+ @) ~
return;6 S& k, M* h: k: Y/ W: y. t) n- c4 u, k
}
+ i* D: _& q0 o- P: K4 z}) x# F$ f K" b, x! M
}# Z+ G. f: ^) g/ K: X
publicvoidunlock(){; m8 z2 @$ a2 C3 K* j
Thread current = Thread.currentThread();
7 y2 \# f6 E- ^" wif(current == owner.get()) {; z, \7 J! \( V7 `" h6 L- e
if(state !=0) {
5 d0 v& G6 b7 k$ G' s7 C, X5 |& ~" b* P. Sstate--;8 }" F5 F4 v% C Q0 b8 X" T* {
}else{
6 F: v# c+ C) A- A' ?owner.compareAndSet(current,null);
$ E9 H. r" y' h}
3 f8 t& \ s, U8 g% T! U}' F, G" S6 Y- Q$ U& s
}
* b& h {* b: h; L" @* V4 s" O}' b b: Q" Q0 v' y" o
在执行每次操作之前,判断当前锁持有者是否是当前对象,采用state计数,不用每次去释放锁。/ F5 o, p" B$ r
ReentrantLock中可重入锁实现$ o- u7 {. D: D& _# P9 K% V
这里看非公平锁的锁获取方法:
- P: r5 r# D( ]4 ]finalbooleannonfairTryAcquire(intacquires){v$ u+ j7 y, e0 Q
finalThread current = Thread.currentThread();
4 f X+ k4 f% r: Q9 L$ I9 Vintc = getState();
+ G( ]* x+ ^ b6 Q. xif(c ==0) {8 D9 t. t% K* m+ a
if(compareAndSetState(0, acquires)) {4 q: c/ D! t' [, z* S
setExclusiveOwnerThread(current);4 R6 a% M, R& ~
returntrue;
. `0 i; |/ z' H. _# P}
- o+ ^% {, s6 R1 `3 |( m}$ I) w% s6 x% K# ^( y- e8 J% M
//就是这里" P; {* B2 N% O$ v H4 s' D7 v
elseif(current == getExclusiveOwnerThread()) {
* @4 @; k9 p$ J8 ~. Y" |9 \ U/ T5 d0 sintnextc = c + acquires;
6 |% D. a6 O* G0 V# h; tif(nextc 0) {// 如果大于0,表示当前线程多次获取了该锁,释放锁通过count减一来模拟% G. V/ R% d7 `+ N L$ ~
count--;
- ]. _# }; q6 l# {% U6 h& U}else{// 如果count==0,可以将锁释放,这样就能保证获取锁的次数与释放锁的次数是一致的了。
. P4 Y4 u' x# }2 C1 I0 Tcas.compareAndSet(cur,null);
' b( f5 M9 x% W9 U, }4 j L# a. @5 r3 l}
- W* t. d0 q' z, A' H/ W}+ |& ^) _3 I7 ~) H
}
" K0 A8 C5 m/ J2 r: s6 v( N7 E& r}
' {3 [5 E* H' }" G' k自旋锁与互斥锁/ W$ |; N1 M8 ~/ s. E* e( H# l
1.自旋锁与互斥锁都是为了实现保护资源共享的机制。' }; [! }& `! _) _% ?. g# D
2.无论是自旋锁还是互斥锁,在任意时刻,都最多只能有一个保持者。$ t6 c) Y! g8 a8 ]; e
3获取互斥锁的线程,如果锁已经被占用,则该线程将进入睡眠状态;获取自旋锁的线程则不会睡眠,而是一直循环等待锁释放。
3 }! J1 x6 q: [# G3 \5 b: _自旋锁总结
! q* M; S2 i7 Z t, O/ F- Y1.自旋锁:线程获取锁的时候,如果锁被其他线程持有,则当前线程将循环等待,直到获取到锁。
6 l4 b+ u& M+ H2 r2.自旋锁等待期间,线程的状态不会改变,线程一直是用户态并且是活动的(active)。z4 j0 W9 \! W8 {/ r0 j. S6 W1 z
3.自旋锁如果持有锁的时间太长,则会导致其它等待获取锁的线程耗尽CPU。5 ]. O7 t& n9 c& ?
4.自旋锁本身无法保证公平性,同时也无法保证可重入性。
( O- s$ Q6 e7 m/ g1 k5.基于自旋锁,可以实现具备公平性和可重入性质的锁。
/ `9 p0 d f- i4 h/ ~2 ~# K# E+ c
2 G2 @( \* ~ ^/ |8 V) G6 \Java吧 收集整理 java论坛 www.java8.com