【區別】synchronized:對象鎖,調用synchronized方法的線程取得該對象鎖後,其他線程需要等待該線程釋放該對象鎖後才能進入該方法。若是持有不同的對象的線程,則可以同時進入該方法。注意:類鎖(全局鎖)synchronized(A.getClass)與對象鎖synchronized(this)的區別【特點】隱式鎖,鎖的範圍是整個方法或代碼塊中,離開該方法/塊後自動釋放鎖
2.重入鎖:ReentrantLock:更靈活的鎖機制,提供可輪詢和可中斷、可定時的鎖獲取機制。持有該鎖的線程可以重複進入該方法(count加1,但只執行一次Method),可以實現更細微的鎖顆粒。【特點】需要顯式的獲取鎖和釋放鎖
使用事例:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds <span style="font-family: Arial, Helvetica, sans-serif;">一次只能有一個線程進入,鎖的顆粒較大</span>
try {
// ... method body
} finally {
lock.unlock()
}
}
}
鎖的顆粒對併發的控制作用是明顯的,例如兩個線程對相同的一個屬性進行操作,A轉賬B的兩個線程中,一個線程需要等待另一個線程釋放該鎖才能進入方法,這樣是沒有問題的。但是兩個線程一個是A轉賬B,一個是C轉賬D,上面的鎖的方法也會等待一個線程執行完畢釋放鎖後,另一個線程才能獲取鎖然後執行,這樣就比較慢了,線程只能排隊執行,不能併發執行。因此需要改進鎖實現方法,實現線程合理的併發控制。實現方法(屬性上加鎖操作,減少鎖的顆粒):
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author : hd
* @Date : 2016/8/18 16:27
* @Version : 1.0.0
*/
/**
* 分段鎖,系統提供一定數量的原始鎖,根據傳入對象的哈希值獲取對應的鎖並加鎖
* 注意:要鎖的對象的哈希值如果發生改變,有可能導致鎖無法成功釋放!!!
*/
public class SegmentLock<T> {
private Integer segments = 16;//默認分段數量
private final Map<Integer, ReentrantLock> lockMap = new HashMap<>();
public SegmentLock() {
init(null, false);
}
public SegmentLock(Integer counts, boolean fair) {
init(counts, fair);
}
private void init(Integer counts, boolean fair) {
if (counts != null) {
segments = counts;
}
for (int i = 0; i < segments; i++) {
lockMap.put(i, new ReentrantLock(fair));
}
}
public void lock(T key) {
ReentrantLock lock = lockMap.get(key.hashCode() % segments);
lock.lock();
}
public void unlock(T key) {
ReentrantLock lock = lockMap.get(key.hashCode() % segments);
lock.unlock();
}
}
另一個事例:三個線程同時進入該鎖的情況public class Test {
//public static volatile int i=10;
static int i=10;
@org.junit.Test
public void testReenTrant(){
final SegmentLock<String> segmentLock=new SegmentLock<String>();
final String key="2016";
for(int j=0;j<3;j++){
new Thread(new Runnable() {
@Override
public void run() {
try{
segmentLock.lock(key);
i=i-1;
System.out.println("lock i:"+i);
}finally {
segmentLock.unlock(key);
}
System.out.println("unlock i:"+i);
}
}).start();
}
}
}
/**
結果:lock i:9
unlock i:9
lock i:8
unlock i:8
根據結果可知:一個線程獲取鎖進入方法執行後,釋放了該鎖,另外兩個線程重複獲取到了鎖synchronized例子:
class Sync {
public synchronized void test() {
System.out.println("test開始..");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("test結束..");
}
}
class MyThread extends Thread {
public void run() {
Sync sync = new Sync();
sync.test();
}
}
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
Thread thread = new MyThread();
thread.start();
}
}
}
運行結果://持有不同對象鎖 的線程同時進入
test開始..
test開始..
test開始..
test結束..
test結束..
test結束..
class MyThread extends Thread {
private Sync sync;
public MyThread(Sync sync) {
this.sync = sync;
}
public void run() {
sync.test();
}
}
public class Main {
public static void main(String[] args) {
Sync sync = new Sync();
for (int i = 0; i < 3; i++) {
Thread thread = new MyThread(sync);
thread.start();
}
}
}
運行結果://持有相同對象鎖 的線程要等另一個線程的對象鎖釋放才能進入
test開始..
test結束..
test開始..
test結束..
test開始..
test結束..
class Sync {
public void test() {
synchronized (Sync.class) {
System.out.println("test開始..");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("test結束..");
}
}
}
class MyThread extends Thread {
public void run() {
Sync sync = new Sync();
sync.test();
}
}
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
Thread thread = new MyThread();
thread.start();
}
}
}
運行結果://對象類鎖,無論創建了多少對象都是屬於這個類,需要等待這個線程釋放鎖,其他線程才能進入,全局鎖
test開始..
test結束..
test開始..
test結束..
test開始..
test結束..
【總結】開發中,常常需要對鎖的粒度對象細化來實現符合邏輯併發控制,ReentrantLock實現了區別於Synchronized的另一種鎖的機制,正如api解釋的一樣
A reentrant mutual exclusion Lock with the same basic behavior and semantics as the implicit monitor lock accessed using synchronized <span style="font-family: Arial, Helvetica, sans-serif;">methods and statements, but with extended capabilities.</span>
和使用synchronized(隱式監視器鎖)和語句訪問具有相同的基本行爲和語義的重入的互斥鎖,但功能更強大。