java基礎----各種型別的鎖
公平鎖:公平鎖就是執行緒遵循先來後到的順序執行。
非公平鎖:非公平鎖就是執行緒會首先嚐試搶奪鎖,如果能搶到,則執行,如果搶不到,則按照公平鎖的規則進行等待。(注意,reentrantLock如果初始化的時候不傳參的話,預設是false,也就是非公平鎖模式,而synchronize也是一個非公平鎖)
可重入鎖:外層函式獲取到鎖後,可以在內層函式中自動獲取對應的鎖,例如:
import java.util.concurrent.locks.ReentrantLock; public class ReentryDemo { private ReentrantLock lock = new ReentrantLock(); public void print1(){ lock.lock(); System.out.println("this is method1"); print2(); } public void print2(){ lock.lock(); System.out.println("this is method2"); } public static void main(String[] args) { ReentryDemo reentryDemo = new ReentryDemo(); reentryDemo.print1(); } }
自旋鎖:是指嘗試獲取鎖的執行緒不會立即阻塞,而是採用迴圈的方式嘗試去獲取鎖,這樣的好處是減少執行緒上下文切換的消耗,缺點是迴圈會消耗CPU。下面提供一個程式碼
import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; public class SpinDemo { AtomicReference<Thread> atomicReference = new AtomicReference<Thread>(); public void myLock() { Thread thread = Thread.currentThread(); System.out.println(Thread.currentThread().getName() + "\tcome into"); while (!atomicReference.compareAndSet(null, thread)) { try { System.out.println(Thread.currentThread().getName() + "\tspinning"); TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + "\tget lock"); } public void myUnLock(){ Thread thread = Thread.currentThread(); System.out.println(Thread.currentThread().getName() + "\trelease lock"); atomicReference.compareAndSet(thread,null); } public static void main(String[] args) throws InterruptedException { SpinDemo spinDemo = new SpinDemo(); new Thread(()->{ spinDemo.myLock(); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } spinDemo.myUnLock(); },"a").start(); TimeUnit.SECONDS.sleep(1); new Thread(()->{ spinDemo.myLock(); spinDemo.myUnLock(); },"b").start(); } }
執行結果:
a come into
a get lock
b come into
b spinning
b spinning
b spinning
b spinning
a release lock
b get lock
b release lock
可以看到,這裡面b執行緒嘗試去獲取鎖的時候,會判斷鎖資源是否有別的執行緒在佔用,如果有別的執行緒在佔用,則會在一個迴圈裡面,反覆地嘗試去獲取鎖。這樣做,執行緒b不會被掛起,減少了上下文切換的開銷,但是假如執行緒b長時間內都獲取不到鎖,那麼他就會不斷地在那裡自旋,這樣反而會增加cpu的消耗,因此在是用自旋鎖的時候,往往需要設定一個自旋時間,當執行緒自旋超過了限定的時間,則需要自動退出來,而這個時間的閾值該怎麼設定,其實就是根據執行緒上下文切換的時間來定。
獨佔鎖:指該鎖只能被一個執行緒鎖持有,對reentrantLock和Synchronized而言都是獨佔鎖。
共享鎖:指該鎖可以被多個執行緒鎖持有,reentrantReadWriteLock的讀是共享鎖,寫鎖是獨佔鎖。
關於讀寫鎖的一個應用場景,下面給出一個例子
import sun.awt.windows.ThemeReader;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
class MyCache {
private volatile Map<String,Object> map = new HashMap<String, Object>();
private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
public void put(String key, Object value)
{
rwLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "\t 正在寫入:" + key);
try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); }
map.put(key,value);
System.out.println(Thread.currentThread().getName() + "\t 寫入完成:" + key);
} catch (Exception e) {
e.printStackTrace();
} finally {
rwLock.writeLock().unlock();
}
}
public void read(String key){
rwLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "\t 正在讀取:" + key);
try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); }
map.get(key);
System.out.println(Thread.currentThread().getName() + "\t 讀取完成:" + key);
} catch (Exception e) {
e.printStackTrace();
} finally {
rwLock.readLock().unlock();
}
}
}
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
for(int i = 0; i < 5; i++){
final int tempInt = i;
new Thread(()->{
myCache.put(tempInt+"",tempInt+"");
},"write"+i).start();
}
for(int i = 0; i < 5; i++){
final int tempInt = i;
new Thread(()->{
myCache.read(tempInt+"");
},"read"+i).start();
}
}
}