JAVA中多線程高併發場景下保證線程安全通常都會考慮加鎖。但是在特殊場景下我們也可以採用java.util.concurrent包提供的線程安全的對象,避免加鎖從而達到高效的目的。
但是,這些線程安全的對象僅僅指的是針對於原子性操作是線程安全的,如果多個方法同時調用無法保證線程安全,只能考慮加鎖。這裏我們舉個列子:假設我們在多線程高併發場景下使用java.util.concurrent.ConcurrentLinkedQueue這個對象來獲取隊列中的元素,可以直接調用poll()方法,不用加鎖也可以保證線程安全,假如我們在獲取的時候需要先判斷隊列是否爲空然後獲取,也就是先調用isEmpty()方法、接着調用poll()方法,這種情況就無法保證線程安全,因爲這裏是兩步操作,無法保證原子性。
ReentrantLock也是多線程併發時候加鎖一種方式,通常會用來和synchronized做比較。我簡單整理一下如下:
1、從使用方法角度對比
- ReentrantLock必須手動釋放鎖,synchronized不用考慮(發生異常自動釋放鎖)
- ReentrantLock可以嘗試鎖定 trylock
- ReentrantLock還可以調用lockInterruptibly方法,可以對線程interrupt方法做成響應
- ReentrantLock可以指定公平鎖Lock lock = new ReentrantLock(true)
2、從性能角度對比
- JDK1.5以前synchronized性能略低於ReentrantLock,包含JDK1.5版本
- JDK1.5以後Java虛擬機對synchronized做了很多優化,增加了偏向鎖、輕量級鎖(多數情況下是自旋鎖)、重量級鎖等細節。性能和ReentrantLock差不多。
我們來分析ReentrantLock 手動加鎖lock()、釋放鎖unlock()、嘗試鎖定tryLock()
package com.reentranlock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* ReentrantLock講解
* lock()、unlock()、嘗試鎖定tryLock()
*
* @author 小輝哥/小輝GE
* <p>
* 2019年8月10日 下午15:30:00
*/
public class ReentrLock {
Lock lock = new ReentrantLock();
public void lock1() {
try {
lock.lock();
System.out.println("lock1方法調用了");
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void lock2() {
try {
lock.lock();
System.out.println("lock2方法調用了");
} finally {
lock.unlock();
}
}
public void lock3() {
boolean status = false;
try {
// status = lock.tryLock();
try {
status = lock.tryLock(2, TimeUnit.SECONDS);
System.out.println("lock3方法嘗試加鎖,返回status爲:" + status);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
if (status)
lock.unlock();
}
}
public static void main(String[] args) {
ReentrLock reentrLock = new ReentrLock();
new Thread(reentrLock::lock1).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(reentrLock::lock2).start();
//嘗試加鎖
new Thread(reentrLock::lock3).start();
}
}
測試輸出結果如下:
結果分析:
1、從輸出結果我們可以看出ReentrantLock手動加鎖是需要釋放的,一般與try/catch/finally一起使用,且在finally手動釋放鎖。
2、lock1方法、lock3方法調用完之後才執行lock2方法,也就是保證了多個線程獲取鎖只能等待其它已獲得鎖的線程執行完畢。
3、lock3方法可以不等到lock1方法執行完畢就調用,這句是tryLock作用,可以在另一個線程加鎖的同時,嘗試去加鎖,能獲取鎖返回true,不能獲取鎖返回false。如果想讓lock3可以獲取鎖,我們可以把tryLock等待時間調大一點即可。
以上代碼僅供參考,如有不當之處,歡迎指出!!!
更多幹貨,歡迎大家關注和聯繫我。期待和大家一起更好的交流、探討技術!!!