公平鎖與非公平鎖
公平鎖:每個線程獲取鎖的機會是公平的,按照申請鎖的順序將線程放進一個FIFO隊列中,誰先申請鎖誰就先獲取鎖,每次有線程來搶佔鎖的時候,都會檢查有沒有等待隊列,如果有,則將該線程加入等待隊列
非公平鎖:線程獲取鎖的機會是不公平的,獲取鎖的線程會隨機搶佔
synchronized實現的鎖是一種非公平鎖,公平鎖可以通過ReentrantLock來實現,同時ReentrantLock也可以實現非公平鎖
// 默認是非公平鎖
Lock lock = new ReentrantLock();
//public ReentrantLock() {
// sync = new NonfairSync();
//}
// 公平鎖
Lock lock = new ReentrantLock(true);
可重入鎖
可重入鎖指在同一個線程中外部方法持有的鎖可以被內部方法獲取
synchronized和ReentrantLock都是可以實現可重入鎖
-
synchronized實現的可重入鎖
public class TestReEntryLock {
public synchronized void set() {
System.out.println(Thread.currentThread().getName()+“set”);
get();
}public synchronized void get() { System.out.println(Thread.currentThread().getName()+"get"); set(); } public static void main(String[] args) { TestReEntryLock lock = new TestReEntryLock(); new Thread(()->{ lock.set(); },"t1").start(); }
}
-
ReentrantLock
public class TestReEntryLock {
Lock lock = new ReentrantLock(); public void set() { try { lock.lock(); System.out.println(Thread.currentThread().getName()+"\t set"); get(); } finally { lock.unlock(); } } public void get() { try { lock.lock(); System.out.println(Thread.currentThread().getName()+"\t get"); } finally { lock.unlock(); } } public static void main(String[] args) { TestReEntryLock lock = new TestReEntryLock(); new Thread(()->{ lock.set(); },"t1").start(); }
}
讀寫鎖
支持共享讀,不支持共享讀寫,和寫。也就是說支持同時讀,但是不支持同時寫。這種鎖可以提高讀的併發性
public class WriteAndReadLock {
ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
// 寫操作
public void write() {
rwl.writeLock().lock();
System.out.println(Thread.currentThread().getName()+"正在寫");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
rwl.writeLock().unlock();
System.out.println(Thread.currentThread().getName()+"寫已經完成");
}
// 讀操作
public void read() {
rwl.readLock().lock();
System.out.println(Thread.currentThread().getName()+"正在讀");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"讀已經完成");
rwl.readLock().unlock();
}
public static void main(String[] args) {
WriteAndReadLock wad = new WriteAndReadLock();
for (int i = 0; i < 3; i++) {
new Thread(()->{
wad.write();
}).start();
}
for (int i = 0; i < 3; i++) {
new Thread(()->{
wad.read();
}).start();
}
}
}
結果:
Thread-0正在寫
Thread-0寫已經完成
Thread-1正在寫
Thread-1寫已經完成
Thread-2正在寫
Thread-2寫已經完成
Thread-3正在讀
Thread-4正在讀
Thread-5正在讀
Thread-3讀已經完成
Thread-4讀已經完成
Thread-5讀已經完成
結論: 每個線程寫操作不能被其它操作所打斷,不支持共享寫。讀操作支持共享讀
自旋鎖
自旋鎖在沒有獲取到鎖的情況下不會馬上將線程阻塞,而是不斷嘗試獲取鎖
public class RoteBySelf {
private AtomicReference<Thread> ar = new AtomicReference<Thread>();
// 手寫一個自旋鎖
public void lock() {
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"\t 嘗試獲取鎖");
// 如果是第一次則獲取鎖成功,跳出while循環
while(!ar.compareAndSet(null,thread)) {
}
System.out.println(thread.getName()+"\t 獲取鎖成功");
}
public void unlock() {
Thread thread = Thread.currentThread();
ar.compareAndSet(thread, null);
System.out.println(thread.getName()+"\t 釋放鎖成功");
}
}
死鎖
死鎖是多個線程爭搶共享資源導致互相等待的一種狀態,如果沒有外力驅使,那麼該狀態會一直存在。
產生死鎖的條件:
- 互斥:一個資源要麼被搶佔,要麼可用,必須是臨界資源
- 請求和保持:線程請求到資源後,繼續申請資源,並且保持原來所持有的資源
- 不可搶佔:已經被分配的資源不可被搶佔
- 環路等待:線程之間互相等待對方釋放所持有的資源
public class DeadDemo implements Runnable{
private Account from;
private Account to;
private int amount;
public DeadDemo(Account from, Account to, int amount) {
this.from = from;
this.to = to;
this.amount = amount;
}
public void run() {
synchronized(from) {
synchronized (to) {
from.amount = from.amount-amount;
to.amount = to.amount+10;
System.out.println("success");
}
}
}
public static void main(String[] args) {
Account a1 = new Account();
Account a2 = new Account();
new Thread(new DeadDemo(a1,a2,10)).start();
new Thread(new DeadDemo(a2,a1,10)).start();
}
}
class Account{
int amount;
}
我的博客:java鎖