synchronized的可重入性
如果一個(gè)同步方法m1中調(diào)用了另一個(gè)同步方法m2,并且這兩個(gè)方法加的是同一把鎖。那么在一個(gè)線程調(diào)用m1時(shí)就得到了這把鎖,m1中調(diào)m2時(shí)發(fā)現(xiàn)是同一個(gè)線程,m2也能得到這把鎖。這是鎖的可重入。
所謂可重入鎖就是拿到這把鎖之后可以再加多道鎖,但鎖定的還是同一對(duì)象,被嵌套調(diào)用的同步方法執(zhí)行完就去一道。
public class SyncTest_03 {
public synchronized void m1() {
System.out.println("m1 start");
m2(); // m2的第二道鎖被釋放后才繼續(xù)往下執(zhí)行
System.out.println("m1 end");
}
public synchronized void m2() {
System.out.println("m2");
}
public static void main(String[] args) {
SyncTest_03 syncTest03 = new SyncTest_03();
new Thread(syncTest03::m1).start();
}
}
- 模擬子類同步方法調(diào)用父類同步方法:
注:子類和父類是同一把鎖
public class SyncTest_04 {
public synchronized void m() {
System.out.println("m start");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m end");
}
public static void main(String[] args) {
Child child = new Child();
new Thread(child::m).start();
}
}
class Child extends SyncTest_04 {
@Override
public synchronized void m() {
System.out.println("child m start");
super.m(); // 調(diào)用父類是同一把鎖
System.out.println("child m end");
}
}
ReentrantLock
ReentrantLock可以替代synchronized,需把原來synchronized的地方換成lock.lock(),加完鎖還需要手動(dòng)解鎖lock.unlock(),并且要在try...finally中手動(dòng)解鎖,保證程序執(zhí)行完或異常后能釋放鎖。synchronized是自動(dòng)解鎖的。
public class T01_ReentrantLock {
Lock lock = new ReentrantLock();
public void m1() {
try {
lock.lock(); // synchronized(this)
System.out.println("m1 start");
m2(); // m2的第二道鎖被釋放后才繼續(xù)往下執(zhí)行
System.out.println("m1 end");
} finally {
lock.unlock(); // 解鎖
}
}
public void m2() {
try {
lock.lock();
System.out.println("m2");
} finally {
lock.unlock(); // 解鎖
}
}
public static void main(String[] args) {
T01_ReentrantLock instance = new T01_ReentrantLock();
new Thread(instance::m1).start();
}
}
既然ReentrantLock與synchronized都可重入,那ReentrantLock有什么用?
tryLock()
ReentrantLock當(dāng)然還有特別的功能,可以使用tryLock進(jìn)行嘗試鎖定,不管鎖定與否,方法都將繼續(xù)執(zhí)行。lock.tryLock()返回boolean類型。
synchronized如果鎖定失敗就阻塞了,但是用ReentrantLock可以自己決定要不要wait。
public class T01_ReentrantLock1 {
Lock lock = new ReentrantLock();
public void m1() {
try {
lock.lock(); // synchronized(this)
System.out.println("m1 start");
TimeUnit.SECONDS.sleep(7);
System.out.println("m1 end");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void m2() {
boolean locked = false;
try {
// locked = lock.tryLock();
locked = lock.tryLock(5, TimeUnit.SECONDS);
System.out.println("m2..." + locked);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (locked) {
lock.unlock();
}
}
}
public static void main(String[] args) {
T01_ReentrantLock1 instance = new T01_ReentrantLock1();
new Thread(instance::m1).start();
new Thread(instance::m2).start();
}
}
m1、m2 start,m1先睡7s,m2 tryLock 5s,此時(shí)鎖還沒被m1釋放,tryLock返回false。若m1睡4s,釋放鎖后m2 tryLock返回true。
lockInterruptibly()
ReentrantLock可以用 lock.lockInterruptibly(),對(duì)interrupt()方法做出響應(yīng)。
線程在請(qǐng)求lock并被阻塞時(shí),如果被interrupt,則此線程會(huì)被喚醒并被要求處理InterruptedException。并且如果線程已經(jīng)被interrupt,再使用lockInterruptibly的時(shí)候,此線程也會(huì)被要求處理interruptedException.
若此線程在運(yùn)行中沒有阻塞, 則不會(huì)處理interruptedException。但此線程的 “打斷標(biāo)志”會(huì)被設(shè)置, 可通過isInterrupted()查看并作出處理。
public class T01_ReentrantLock2 {
Lock lock = new ReentrantLock();
public void m1() {
try {
lock.lock(); // synchronized(this)
System.out.println("m1 start");
TimeUnit.SECONDS.sleep(10);
System.out.println("m1 end");
} catch (InterruptedException e) {
System.out.println("Interrupted!!");
} finally {
lock.unlock();
}
}
public void m2() {
boolean locked = false;
try {
lock.lockInterruptibly(); // 可以對(duì)interrupt()方法做出響應(yīng)
System.out.println("m2 start");
TimeUnit.SECONDS.sleep(5);
System.out.println("m2 end");
} catch (InterruptedException e) {
System.out.println("m2 Interrupted!!");
} finally {
if (locked) {
lock.unlock();
}
}
}
public static void main(String[] args) {
T01_ReentrantLock2 instance = new T01_ReentrantLock2();
Thread t1 = new Thread(instance::m1);
Thread t2 = new Thread(instance::m2);
t1.start();
t2.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.interrupt(); // 打斷線程2的等待
}
}
公平鎖與非公平鎖
- 什么是公平鎖與非公平鎖?
如果是公平鎖,一個(gè)線程會(huì)先檢查等待隊(duì)列里是否有其它線程,如果有進(jìn)入隊(duì)列排隊(duì),等別的線程先運(yùn)行;
如果是非公平鎖,一個(gè)線程不管其它線程是否先執(zhí)行,而是上來就競(jìng)爭(zhēng)鎖。
ReentrantLock默認(rèn)是非公平鎖,但是可指定為公平鎖。
// 參數(shù)為true是公平鎖
ReentrantLock lock = new ReentrantLock(true);
總結(jié)
- ReentrantLock可以替代synchronized,也可重入,底層是cas
- tryLock:嘗試鎖定,不管鎖定與否,都將繼續(xù)執(zhí)行。tryLock()返回boolean類型
- lockInterruptibly:被interrupt()后,線程會(huì)被喚醒并被要求處理InterruptedException
- ReentrantLock默認(rèn)為非公平鎖,new ReentrantLock(true) 設(shè)置為公平鎖