java 共享锁 独占锁_java中的公平锁、非公平锁、可重入锁、递归锁、自旋锁、独占锁和共享锁...
一、公平鎖與非公平鎖
1.1 概述
公平鎖:是指多個(gè)線程按照申請鎖的順序來獲取鎖。
非公平鎖:是指在多線程獲取鎖的順序并不是按照申請鎖的順序,有可能后申請的線程比先申請的線程優(yōu)先獲取到鎖,在高并發(fā)的情況下,有可能造成優(yōu)先級反轉(zhuǎn)或者饑餓現(xiàn)象。饑餓現(xiàn)象就是低優(yōu)先級的線程可能一直拿不到鎖,而一直處于等待狀態(tài)。
1.2 區(qū)別
公平鎖:Threads acquire a fair lock in the order in which they requested it.
公平鎖,就是很公平,在并發(fā)環(huán)境中,每個(gè)線程在獲取鎖時(shí)會(huì)先查看此鎖維護(hù)的等待隊(duì)列,如果為空,或者當(dāng)前線程是等待隊(duì)列的第一個(gè),就占有鎖,否則就會(huì)加入到等待隊(duì)列中,以后會(huì)按照 FIFO 的規(guī)則從隊(duì)列中取到自己。
非公平鎖:a nonfair lock permits barging: threads requesting a lock can jump ahead of the queue of waiting threads if the lock
happens to be available when it is requested.
非公平鎖比較粗魯,上來就直接嘗試占有鎖,如果嘗試失敗,就再采用類似公平鎖那種方式。而且,非公平鎖比公平鎖的吞吐量大。
1.3 Java 中的一些公平鎖和非公平鎖
1. java 中的 ReentrantLock,默認(rèn)是非公平鎖,當(dāng)參數(shù) fair 為 true 時(shí),就是公平鎖。
1 /**
2 * Creates an instance of {@code ReentrantLock}.
3 * This is equivalent to using {@code ReentrantLock(false)}.
4 */
5 public ReentrantLock() {
6 sync = new NonfairSync();
7 }
8
9 /**
10 * Creates an instance of {@code ReentrantLock} with the
11 * given fairness policy.
12 *
13 * @param fair {@code true} if this lock should use a fair ordering policy
14 */
15 public ReentrantLock(boolean fair) {
16 sync = fair ? new FairSync() : new NonfairSync();
17 }
2. synchronized 也是一種非公平鎖。
二、可重入鎖與不可重入鎖
2.1 概述
可重入鎖(也叫做遞歸鎖):
指的是同一線程外層函數(shù)獲得鎖之后,內(nèi)層遞歸函數(shù)仍然能獲取該鎖的代碼,在同一個(gè)線程在外層方法獲取鎖的時(shí)候,在進(jìn)入內(nèi)層方法會(huì)自動(dòng)獲取鎖,也就是說,線程可以進(jìn)入任何一個(gè)它已經(jīng)擁有的鎖所同步著的代碼塊。可重入鎖最大的作用就是避免死鎖。
不可重入鎖,即若當(dāng)前線程執(zhí)行某個(gè)方法已經(jīng)獲取了該鎖,那么在方法中嘗試再次獲取鎖時(shí),就會(huì)獲取不到被阻塞。
2.2 java 中的可重入鎖
2.2.1 synchronized 鎖
1 class Phone {
2 public synchronized void sendSMS() {
3 System.out.println(Thread.currentThread().getName() + "send SMS...");
4 sendEmail();
5 }
6
7 public synchronized void sendEmail() {
8 System.out.println(Thread.currentThread().getName() + "send email...");
9 }
10 }
11
12 public class ReentrantLockDemo {
13
14 public static void main(String[] args) {
15 Phone phone = new Phone();
16
17 new Thread(() -> {
18 phone.sendSMS();
19 }, "Thread1").start();
20
21 new Thread(() -> {
22 phone.sendSMS();
23 }, "Thread2").start();
24 }
25 }
2.2.2 ReentrantLock
1 class Phone implements Runnable {
2 Lock lock = new ReentrantLock();
3
4 @Override
5 public void run() {
6 get();
7 }
8
9 public void get() {
10 lock.lock();
11 try {
12 System.out.println(Thread.currentThread().getName() + "get method...");
13 set();
14 } finally {
15 lock.unlock();
16 }
17 }
18
19 public void set() {
20 lock.lock();
21 try {
22 System.out.println(Thread.currentThread().getName() + "set method...");
23 } finally {
24 lock.unlock();
25 }
26 }
27 }
28
29 public class ReentrantLockDemo {
30
31 public static void main(String[] args) {
32 Phone phone = new Phone();
33
34 Thread thread3 = new Thread(phone, "Thread3");
35 Thread thread4 = new Thread(phone, "Thread4");
36 thread3.start();
37 thread4.start();
38 }
39 }
2.3 面試題
使用 ReentrantLock 時(shí),如果加入兩層鎖呢,程序是直接報(bào)編譯錯(cuò)誤,還是正常運(yùn)行,正常運(yùn)行的話,能得到預(yù)期的結(jié)果嗎?
1 class Phone implements Runnable {
2
3 // ...
4
5 public void get() {
6 lock.lock();
7 lock.lock();
8 try {
9 System.out.println(Thread.currentThread().getName() + "get method...");
10 set();
11 } finally {
12 lock.unlock();
13 lock.unlock();
14 }
15 }
16
17 // ...
18 }
當(dāng)缺少 unlock() 時(shí)(也就是,lock 和 unlock不是一一對應(yīng),lock 比 unlock 多 ),程序不會(huì)報(bào)編譯錯(cuò)誤,但得不到預(yù)期的結(jié)果,從下面可以看出,程序一直處于運(yùn)行的狀態(tài):
當(dāng)缺少 lock() 時(shí)(也就是,unlock 比 lock 多 ),此時(shí),程序也不會(huì)報(bào)編譯錯(cuò)誤,控制臺(tái)也輸出了結(jié)果,但是拋出了 IllegalMonitorStateException 異常。
三、自旋鎖
3.1 概述
自旋鎖是指嘗試獲取鎖的線程不會(huì)立即阻塞,而是采用循環(huán)的方式去嘗試獲取鎖,這樣的好處是減少線程上下文切換的消耗,缺點(diǎn)是循環(huán)會(huì)消耗CPU。
3.2 java 中的自旋鎖
1 // Unsafe.java
2 public final int getAndAddInt(Object var1, long var2, int var4) {
3 int var5;
4 do {
5 var5 = this.getIntVolatile(var1, var2);
6 } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
7
8 return var5;
9 }
3.3 手寫一個(gè)自旋鎖
1 public class SpinLockDemo {
2
3 AtomicReference atomicReference = new AtomicReference<>();
4
5 public void myLock() {
6 Thread thread = Thread.currentThread();
7 System.out.println(thread.getName() + "come in...");
8 while (!atomicReference.compareAndSet(null, thread)) {
9
10 }
11 }
12
13 public void myUnLock() {
14 Thread thread = Thread.currentThread();
15 atomicReference.compareAndSet(thread, null);
16 System.out.println(thread.getName() + "come out...");
17 }
18
19 public static void main(String[] args) {
20
21 SpinLockDemo spinLockDemo = new SpinLockDemo();
22
23 new Thread(() -> {
24 spinLockDemo.myLock();
25 try {
26 TimeUnit.SECONDS.sleep(5);
27 } catch (InterruptedException e) {
28 e.printStackTrace();
29 }
30 spinLockDemo.myUnLock();
31 }, "Thread1").start();
32
33 try {
34 TimeUnit.SECONDS.sleep(1);
35 } catch (InterruptedException e) {
36 e.printStackTrace();
37 }
38
39 new Thread(() -> {
40 spinLockDemo.myLock();
41 try {
42 TimeUnit.SECONDS.sleep(1);
43 } catch (InterruptedException e) {
44 e.printStackTrace();
45 }
46 spinLockDemo.myUnLock();
47 }, "Thread2").start();
48 }
49 }
四、寫鎖(獨(dú)占鎖)、讀鎖(共享鎖)和互斥鎖
4.1 概述
獨(dú)占鎖:指該鎖一次只能被一個(gè)線程所持有。對 ReentrantLock 和 Synchronized 而言都是獨(dú)占鎖。
共享鎖:指該鎖可被多個(gè)線程所持有。
對 ReentrantReadWriteLock 其讀鎖是共享鎖,其寫鎖是獨(dú)占鎖。
讀鎖的共享鎖可保證并發(fā)讀是非常高效的,讀寫,寫讀,寫寫的過程是互斥的。
4.2 示例(模擬緩存)
4.2.1 加鎖前:
數(shù)據(jù)寫入的時(shí)候,被打斷:
1 class MyCache {
2
3 private volatile Map map = new HashMap<>();
4
5 public void put(String key, Object value) {
6 System.out.println(Thread.currentThread().getName() + "正在寫入:" + key);
7 try {
8 TimeUnit.MILLISECONDS.sleep(300);
9 } catch (InterruptedException e) {
10 e.printStackTrace();
11 }
12 map.put(key, value);
13 System.out.println(Thread.currentThread().getName() + "寫入完成");
14 }
15
16 public void get(String key) {
17 System.out.println(Thread.currentThread().getName() + "正在讀取");
18 try {
19 TimeUnit.MILLISECONDS.sleep(300);
20 } catch (InterruptedException e) {
21 e.printStackTrace();
22 }
23 Object result = map.get(key);
24 System.out.println(Thread.currentThread().getName() + "讀取完成:" + result);
25 }
26 }
27
28 public class ReadWriteLockDemo {
29
30 public static void main(String[] args) {
31 MyCache myCache = new MyCache();
32
33 for (int i = 1; i <= 5; i++) {
34 final int temp = i;
35 new Thread(() -> {
36 myCache.put(temp + "", temp + "");
37 }, String.valueOf(i)).start();
38 }
39
40 for (int i = 1; i <= 5; i++) {
41 final int temp = i;
42 new Thread(() -> {
43 myCache.get(temp + "");
44 }, String.valueOf(i)).start();
45 }
46 }
47 }
4.2.2 加鎖后:
寫入時(shí)正常,不會(huì)中斷;讀取時(shí),可以共享鎖。
1 class MyCache {
2
3 private volatile Map map = new HashMap<>();
4 private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
5
6 public void put(String key, Object value) {
7 rwLock.writeLock().lock();
8 try {
9 System.out.println(Thread.currentThread().getName() + "正在寫入:" + key);
10 try {
11 TimeUnit.MILLISECONDS.sleep(300);
12 } catch (InterruptedException e) {
13 e.printStackTrace();
14 }
15 map.put(key, value);
16 System.out.println(Thread.currentThread().getName() + "寫入完成");
17 } catch (Exception e) {
18 e.printStackTrace();
19 } finally {
20 rwLock.writeLock().unlock();
21 }
22 }
23
24 public void get(String key) {
25 rwLock.readLock().lock();
26 try {
27 System.out.println(Thread.currentThread().getName() + "正在讀取");
28 try {
29 TimeUnit.MILLISECONDS.sleep(300);
30 } catch (InterruptedException e) {
31 e.printStackTrace();
32 }
33 Object result = map.get(key);
34 System.out.println(Thread.currentThread().getName() + "讀取完成:" + result);
35 } catch (Exception e) {
36 e.printStackTrace();
37 } finally {
38 rwLock.readLock().unlock();
39 }
40 }
41 }
總結(jié)
以上是生活随笔為你收集整理的java 共享锁 独占锁_java中的公平锁、非公平锁、可重入锁、递归锁、自旋锁、独占锁和共享锁...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php快速排序实现代码,快速排序的算法p
- 下一篇: 802d简明调试手册_SINUMERIK