①. 可重入鎖
1>. 可重入鎖(遞回鎖)
-
①. 指的是同一執行緒外層函式獲得鎖后,再進入該執行緒的內層方法會自動獲取鎖 (
前提,鎖物件是同一個物件)
類似于家里面的大門,進入之后可以進入廁所、廚房等 -
②. Java中ReentranLock(顯示鎖)和synchronized(隱式鎖)都是可重入鎖,可重入鎖的一個優點是可在一定程度避免死鎖
-
③.
隱式鎖:(即synchronized關鍵字使用的鎖)默認是可重入鎖(同步塊、同步方法)
原理如下:掌握
- 每個鎖物件擁有一個鎖計數器和一個指向持有該鎖的執行緒的指標
- 當執行monitorenter時,如果目標鎖物件的計數器為零,那么說明它沒有被其他執行緒持有,Java虛擬機會將該鎖物件的持有執行緒設定為當前執行緒,并且將其計數器加1,否則需要等待,直至持有執行緒釋放該鎖
- 當執行monitorexit時,Java虛擬機則鎖物件的計數器減1,計數器為零代表鎖已經被釋放

//1.同步塊
public class SychronizedDemo {
Object object=new Object();
public void sychronizedMethod(){
new Thread(()->{
synchronized (object){
System.out.println(Thread.currentThread().getName()+"\t"+"外層....");
synchronized (object){
System.out.println(Thread.currentThread().getName()+"\t"+"中層....");
synchronized (object){
System.out.println(Thread.currentThread().getName()+"\t"+"內層....");
}
}
}
},"A").start();
}
public static void main(String[] args) {
new SychronizedDemo().sychronizedMethod();
/*
輸出結果:
A 外層....
A 中層....
A 內層....
* */
}
}
//2.同步代碼塊
class Phone{
public synchronized void sendSms() throws Exception{
System.out.println(Thread.currentThread().getName()+"\tsendSms");
sendEmail();
}
public synchronized void sendEmail() throws Exception{
System.out.println(Thread.currentThread().getName()+"\tsendEmail");
}
}
/**
* Description:
* 可重入鎖(也叫做遞回鎖)
* 指的是同一執行緒外層函式獲得鎖后,內層遞回函式任然能獲取該鎖的代碼
* 在同一執行緒外外層方法獲取鎖的時候,在進入內層方法會自動獲取鎖
* 也就是說,執行緒可以進入任何一個它已經標記的鎖所同步的代碼塊
* **/
public class ReenterLockDemo {
/**
* t1 sendSms
* t1 sendEmail
* t2 sendSms
* t2 sendEmail
* @param args
*/
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
try {
phone.sendSms();
} catch (Exception e) {
e.printStackTrace();
}
},"t1").start();
new Thread(()->{
try {
phone.sendSms();
} catch (Exception e) {
e.printStackTrace();
}
},"t2").start();
}
}
- ④.
顯示鎖:(即lock)也有ReentrantLock這樣的可重入鎖
(注意:有多少個lock,就有多少個unlock,他們是配對使用的;如果多一個或者少一個會使得其他執行緒處于等待狀態)
class Phone2{
static ReentrantLock reentrantLock=new ReentrantLock();
public static void sendSms(){
reentrantLock.lock();
/*
//reentrantLock.lock();
注意有多少個lock,就有多少個unlock,他們是配對使用的
如果多了一個lock(),那么會出現執行緒B一直處于等待狀態
* */
reentrantLock.lock();
try {
System.out.println(Thread.currentThread().getName()+"\t"+"sendSms");
sendEmails();
}catch (Exception e){
e.printStackTrace();
}finally {
reentrantLock.unlock();
}
}
private static void sendEmails() {
reentrantLock.lock();
try {
System.out.println(Thread.currentThread().getName()+"\t"+"sendEmails...");
}catch (Exception e){
e.printStackTrace();
}finally {
reentrantLock.unlock();
}
}
}
public class ReentrantLockDemo {
public static void main(String[] args) {
Phone2 phone2=new Phone2();
new Thread(()->{phone2.sendSms();},"A").start();
new Thread(()->{phone2.sendSms();},"B").start();
}
}
②. 為什么要使用LockSupport
2>.為什么要使用LockSupport(先來了解下傳統的等待喚醒機制)
- ①. 3種讓執行緒等待喚醒的方法
- 使用Object中的wait()方法讓執行緒等待,使用Object中的notify方法喚醒執行緒
- 使用JUC包中Condition的await()方法讓執行緒等待,使用signal()方法喚醒執行緒
- LockSupport類可以阻塞當前執行緒以及喚醒指定被阻塞的執行緒
- ②. Object類中wait( )和notify( )實作執行緒的等待喚醒
- wait和notify方法必須要在同步塊或同步方法里且成對出現使用, wait和notify方法兩個都去掉同步代碼塊后看運行效果出現例外情況:
Exception in thread “A” Exception in thread “B”
java.lang.IllegalMonitorStateException - 先wait后notify才可以(如果先notify后wait會出現另一個執行緒一直處于等待狀態)
- synchronized是關鍵字屬于JVM層面,monitorenter(底層是通過monitor物件來完成,其實wait/notify等方法也依賴monitor物件只能在同步塊或方法中才能呼叫wait/notify等方法)
public class SynchronizedDemo {
//等待執行緒
public void waitThread(){
// 1.如果將synchronized (this){}注釋,會拋出例外,因為wait和notify一定要在同步塊或同步方法中
synchronized (this){
try {
System.out.println(Thread.currentThread().getName()+"\t"+"coming....");
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"\t"+"end....");
}
}
//喚醒執行緒
public void notifyThread(){
synchronized (this){
System.out.println("喚醒A執行緒....");
notify();
}
}
public static void main(String[] args) {
SynchronizedDemo synchronizedDemo = new SynchronizedDemo();
new Thread(()->{
// 2.如果把下行這句代碼打開,先notify后wait,會出現A執行緒一直處于等待狀態
// try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) {e.printStackTrace();}
synchronizedDemo.waitThread();
},"A").start();
new Thread(()->{
synchronizedDemo.notifyThread();
},"B").start();
}
}
- ③. Condition介面中的await和signal方法實作執行緒等待和喚醒
(出現的問題和object中wait和notify一樣)
public class LockDemo {
static Object object=new Object();
public static void main(String[] args) {
Lock lock=new ReentrantLock();
Condition condition = lock.newCondition();
new Thread(()->{
//如果把下行這句代碼打開,先signal后await,會出現A執行緒一直處于等待狀態
//try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) {e.printStackTrace();}
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"\t"+"coming....");
condition.await();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
System.out.println(Thread.currentThread().getName()+"\t"+"END....");
},"A").start();
new Thread(()->{
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"\t"+"喚醒A執行緒****");
condition.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
},"B").start();
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/187224.html
標籤:其他
