1、公平鎖、非公平鎖
-
是什么
公平鎖就是先來后到、非公平鎖就是允許加塞,
Lock lock = new ReentrantLock(Boolean fair);默認(rèn)非公平。==公平鎖==是指多個(gè)線程按照申請鎖的順序來獲取鎖,類似排隊(duì)打飯。
==非公平鎖==是指多個(gè)線程獲取鎖的順序并不是按照申請鎖的順序,有可能后申請的線程優(yōu)先獲取鎖,在高并發(fā)的情況下,有可能會(huì)造成優(yōu)先級(jí)反轉(zhuǎn)或者節(jié)現(xiàn)象。
-
兩者區(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.
非公平鎖比較粗魯,上來就直接嘗試占有額,如果嘗試失敗,就再采用類似公平鎖那種方式。
-
2、可重入鎖(遞歸鎖)
-
遞歸鎖是什么
指的時(shí)同一線程外層函數(shù)獲得鎖之后,內(nèi)層遞歸函數(shù)仍然能獲取該鎖的代碼,在同一個(gè)線程在外層方法獲取鎖的時(shí)候,在進(jìn)入內(nèi)層方法會(huì)自動(dòng)獲取鎖,也就是說,線程可以進(jìn)入任何一個(gè)它已經(jīng)擁有的鎖所同步著的代碼塊
ReentrantLock/Synchronized 就是一個(gè)典型的可重入鎖
可重入鎖最大的作用是避免死鎖
-
代碼示例
package com.jian8.juc.lock; #### public static void main(String[] args) { Phone phone = new Phone(); new Thread(() -> { try { phone.sendSMS(); } catch (Exception e) { e.printStackTrace(); } }, "Thread 1").start(); new Thread(() -> { try { phone.sendSMS(); } catch (Exception e) { e.printStackTrace(); } }, "Thread 2").start(); } } class Phone{ public synchronized void sendSMS()throws Exception{ System.out.println(Thread.currentThread().getName()+"\t -----invoked sendSMS()"); Thread.sleep(3000); sendEmail(); } public synchronized void sendEmail() throws Exception{ System.out.println(Thread.currentThread().getName()+"\t +++++invoked sendEmail()"); } }package com.jian8.juc.lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockDemo { public static void main(String[] args) { Mobile mobile = new Mobile(); new Thread(mobile).start(); new Thread(mobile).start(); } } class Mobile implements Runnable{ Lock lock = new ReentrantLock(); @Override public void run() { get(); } public void get() { lock.lock(); try { System.out.println(Thread.currentThread().getName()+"\t invoked get()"); set(); }finally { lock.unlock(); } } public void set(){ lock.lock(); try{ System.out.println(Thread.currentThread().getName()+"\t invoked set()"); }finally { lock.unlock(); } } }
3、獨(dú)占鎖(寫鎖)/共享鎖(讀鎖)/互斥鎖
-
概念
獨(dú)占鎖:指該鎖一次只能被一個(gè)線程所持有,對ReentrantLock和Synchronized而言都是獨(dú)占鎖
-
共享鎖:只該鎖可被多個(gè)線程所持有
ReentrantReadWriteLock其讀鎖是共享鎖,寫鎖是獨(dú)占鎖
互斥鎖:讀鎖的共享鎖可以保證并發(fā)讀是非常高效的,讀寫、寫讀、寫寫的過程是互斥的
-
代碼示例
package com.jian8.juc.lock; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * 多個(gè)線程同時(shí)讀一個(gè)資源類沒有任何問題,所以為了滿足并發(fā)量,讀取共享資源應(yīng)該可以同時(shí)進(jìn)行。 * 但是 * 如果有一個(gè)線程象取寫共享資源來,就不應(yīng)該自由其他線程可以對資源進(jìn)行讀或?qū)? * 總結(jié) * 讀讀能共存 * 讀寫不能共存 * 寫寫不能共存 */ public class ReadWriteLockDemo { public static void main(String[] args) { MyCache myCache = new MyCache(); for (int i = 1; i <= 5; i++) { final int tempInt = i; new Thread(() -> { myCache.put(tempInt + "", tempInt + ""); }, "Thread " + i).start(); } for (int i = 1; i <= 5; i++) { final int tempInt = i; new Thread(() -> { myCache.get(tempInt + ""); }, "Thread " + i).start(); } } } class MyCache { private volatile Map<String, Object> map = new HashMap<>(); private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); /** * 寫操作:原子+獨(dú)占 * 整個(gè)過程必須是一個(gè)完整的統(tǒng)一體,中間不許被分割,不許被打斷 * * @param key * @param value */ public void put(String key, Object value) { rwLock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName() + "\t正在寫入:" + key); TimeUnit.MILLISECONDS.sleep(300); map.put(key, value); System.out.println(Thread.currentThread().getName() + "\t寫入完成"); } catch (Exception e) { e.printStackTrace(); } finally { rwLock.writeLock().unlock(); } } public void get(String key) { rwLock.readLock().lock(); try { System.out.println(Thread.currentThread().getName() + "\t正在讀?。? + key); TimeUnit.MILLISECONDS.sleep(300); Object result = map.get(key); System.out.println(Thread.currentThread().getName() + "\t讀取完成: " + result); } catch (Exception e) { e.printStackTrace(); } finally { rwLock.readLock().unlock(); } } public void clear() { map.clear(); } }
4、自旋鎖
-
spinlock
是指嘗試獲取鎖的線程不會(huì)立即阻塞,而是采用循環(huán)的方式去嘗試獲取鎖,這樣的好處是減少線程上下文切換的消耗,缺點(diǎn)是循環(huán)會(huì)消耗CPU
public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; }手寫自旋鎖:
package com.jian8.juc.lock; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; /** * 實(shí)現(xiàn)自旋鎖 * 自旋鎖好處,循環(huán)比較獲取知道成功位置,沒有類似wait的阻塞 * * 通過CAS操作完成自旋鎖,A線程先進(jìn)來調(diào)用mylock方法自己持有鎖5秒鐘,B隨后進(jìn)來發(fā)現(xiàn)當(dāng)前有線程持有鎖,不是null,所以只能通過自旋等待,知道A釋放鎖后B隨后搶到 */ public class SpinLockDemo { public static void main(String[] args) { SpinLockDemo spinLockDemo = new SpinLockDemo(); new Thread(() -> { spinLockDemo.mylock(); try { TimeUnit.SECONDS.sleep(3); }catch (Exception e){ e.printStackTrace(); } spinLockDemo.myUnlock(); }, "Thread 1").start(); try { TimeUnit.SECONDS.sleep(3); }catch (Exception e){ e.printStackTrace(); } new Thread(() -> { spinLockDemo.mylock(); spinLockDemo.myUnlock(); }, "Thread 2").start(); } //原子引用線程 AtomicReference<Thread> atomicReference = new AtomicReference<>(); public void mylock() { Thread thread = Thread.currentThread(); System.out.println(Thread.currentThread().getName() + "\t come in"); while (!atomicReference.compareAndSet(null, thread)) { } } public void myUnlock() { Thread thread = Thread.currentThread(); atomicReference.compareAndSet(thread, null); System.out.println(Thread.currentThread().getName()+"\t invoked myunlock()"); } }
鎖升級(jí)
偏向鎖--》輕量級(jí)鎖 (自旋鎖) --》重量級(jí)鎖
鎖優(yōu)化
鎖粗化
鎖消除
適應(yīng)性自旋:適應(yīng)性自旋,線程如果自旋成功了,則下次自旋的次數(shù)會(huì)更多,如果自旋失敗了,則自旋的次數(shù)就會(huì)減少。