更新時間:2022-08-08 12:19:31 來源:動力節點 瀏覽1238次
在Java教程中,高并發編程是一定要學習的,下面動力節點小編來為大家進行介紹。
synchronized同步類的不同實例方法,爭搶的是同一個monitor的lock,而與之關聯的引用是ThisMonitor的實例引用
package JavaConcurrencyInPractice.book.charpter3;
import java.util.concurrent.TimeUnit;
/**
* @program: JavaLife
* @author: JiaLe Hu
* @create: 2020-12-09 10:38
**/
public class ThisMonitor {
public synchronized void method1() {
System.out.println(Thread.currentThread().getName() + "enter to method1");
try {
TimeUnit.MINUTES.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void method2() {
synchronized (this) {
System.out.println(Thread.currentThread().getName() + "enter to method1");
try {
TimeUnit.MINUTES.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// public synchronized void method2() {
// System.out.println(Thread.currentThread().getName() + "enter to method2");
// try {
// TimeUnit.MINUTES.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
public static void main(String[] args) {
ThisMonitor thisMonitor = new ThisMonitor();
new Thread(thisMonitor::method1, "T1").start();
new Thread(thisMonitor::method2, "T2").start();
}
}
synchronized同步某個類的不同靜態方法爭搶的鎖也是同一個monitor的lock,該monitor關聯的引用是ClassMonitor.class實例
package JavaConcurrencyInPractice.book.charpter3;
import java.util.concurrent.TimeUnit;
/**
* @program: JavaLife
* @author: JiaLe Hu
* @create: 2020-12-09 10:49
**/
public class ClassMonitor {
public static synchronized void method1() {
System.out.println(Thread.currentThread().getName() + "enter to method1");
try {
TimeUnit.MINUTES.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// public static synchronized void method2() {
// System.out.println(Thread.currentThread().getName() + "enter to method1");
// try {
// TimeUnit.MINUTES.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
public static synchronized void method2() {
synchronized (ClassMonitor.class) {
System.out.println(Thread.currentThread().getName() + "enter to method1");
try {
TimeUnit.MINUTES.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
重入
內置鎖是可以重入的,如果某個線程試圖獲得一個已經由它自己持有的鎖,那么這個請求就會成功。重入意味著獲取鎖的操作的粒度是線程。
Wait And notify
wait 方法是可中斷的,當前線程一旦調用了wait方法進入阻塞狀態,其他線程是可以使用interrupt方法將其打斷。
線程執行了某個對象的wait方法,會加入與之對應的wait set中,每一個對象的monitor都有一個與之關聯的wait set
必須在同步方法中使用wait和notify,因為執行wait和notify的前提條件是必須持有同步方法的monitor的所有權
wait會釋放掉monitor的鎖
synchronized 的缺陷
不能控制阻塞的時間
同步方法不能被中斷,不能像wait和sleep方法一樣,能夠捕獲得到中斷信號
package LeetCode;
import java.util.concurrent.TimeUnit;
/**
* @program: JavaLife
* @author: JiaLe Hu
* @create: 2020-12-10 09:32
**/
public class synchronizedDefect {
public synchronized void syncMethod() {
try {
System.out.println(Thread.currentThread().getName() + " get this monitor");
TimeUnit.HOURS.sleep(1);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " is interrupted");
}
}
public static void main(String[] args) throws InterruptedException {
synchronizedDefect synchronizedDefect = new synchronizedDefect();
Thread t1 = new Thread(synchronizedDefect::syncMethod, "t1");
t1.start();
TimeUnit.MILLISECONDS.sleep(2);
Thread t2 = new Thread(synchronizedDefect::syncMethod, "t2");
t2.start();
TimeUnit.MILLISECONDS.sleep(2);
t2.interrupt();
System.out.println(t2.isInterrupted()); // true
System.out.println(t2.getState()); // BLOCKED
}
}
該ThreadGroup如果有父ThreadGroup,則直接調用父Group的uncaughtException方法
如果設置了全局默認的UncaughtException,則會調用全局的uncaughtException方法
若既沒有父ThreadGroup,也沒有全局默認的UncaughtException,直接將異常的堆棧信息定向到System.err中
Hook注入(Runtime)
package JavaConcurrencyInPractice.book.charpter4;
import java.util.concurrent.TimeUnit;
/**
* @program: JavaLife
* @author: JiaLe Hu
* @create: 2020-12-11 11:05
**/
public class ThreadHook {
public static void main(String[] args) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
System.out.println("The hook thread 1 is running ");
TimeUnit.SECONDS.sleep(1);
System.out.println("The hook thread 1 will exit");
} catch (InterruptedException e) {
e.printStackTrace();
}
}));
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
System.out.println("The hook thread 2 is running ");
TimeUnit.SECONDS.sleep(2);
System.out.println("The hook thread 2 will exit");
} catch (InterruptedException e) {
e.printStackTrace();
}
}));
System.out.println("program is exiting");
}
}
Hook線程應用場景以及注意事項
Hook線程只有在收到推出信號的時候會被執行,如果在kill的時候使用了參數-9,那么Hook線程不會執行,進程將立即退出,因此lock文件不會被刪除
Hook線程中也可以執行一些資源釋放的工作,比如關閉文件句柄、socket鏈接、數據庫connection
盡量不要在Hook線程中執行一些耗時非常長的操作。因此會導致程序遲遲不能退出。
使用new關鍵字會導致類的初始化
訪問類的靜態變量
訪問類的靜態方法,會導致類的初始化
對某個類進行反射
初始化子類會導致父類的初始化(通過子類的靜態變量只會導致父類的初始化)
啟動類
除了上述的6種情況,其余都被稱為被動使用,不會導致類的加載和初始化
類的加載階段
class文件種的二進制數據讀取到內存中,然后將該字節流所代表的靜態存儲結構轉換為方法區中的運行時的數據結構,并且在堆內存中生成一個該類的java.lang.Class對象,作為訪問方法區數據結構的入口。加載過程常伴隨著連接階段交叉工作。
類初始化的階段
包含了所有類變量的賦值動作和靜態語句塊的執行代碼
靜態語句塊只能對后面的靜態變量進行賦值,但是不能對其訪問。父類的靜態變量總是能夠得到優先賦值。
根加載器
根加載器又被稱為Bootstrap類加載器,該類加載器是最為頂層的加載器,其沒有父加載器,它是由C++編寫的,主要負責虛擬機核心類庫的加載-Xbootclasspath來指定根加載器的路徑
擴展類加載器
擴展類加載器的父加載器是根加載器,主要用于加載JAVA_HOME下jre/lb/ext子目錄里面的類庫。由純Java語言實現
系統類加載器
負責加載classpath下的類庫資源
如果大家想了解更多相關知識,可以關注一下動力節點的Java多線程并發編程,里面有更豐富的知識等著大家去學習,希望對大家能夠有所幫助。
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習