單例模式
立即加載(餓漢模式)
立即加載就是使用類的時候已經將對象創建完畢,常見的實現辦法就是聲明的時候直接new實例化。
// 加載類時候創建對象:靜態變量
public class A {
private static A a = new A(); // 加載類時候就創建
private A(){}
public static A getInstance(){
return a;
}
}
// 加載類時候創建對象:靜態代碼塊
public class A {
private static A a;
static {
a = new A(); // 加載類時候就創建
}
private A(){}
public static A getInstance(){
return a;
}
}
// 靜態內置類實現
public class A() {
private static class AHandler {
private static A a = new A(); // 加載類時候就創建
}
private A(){}
public static A getInstance(){
return AHandler.a;
}
}
// 利用枚舉類的構造方法,枚舉類構造方法會在加載時候被自動調用
public class A() {
// 這麼封裝是爲了避免AEnum沒有特別作用,他只是封裝A內部作爲單例的實現
private enum AEnum {
bFactory;
private B b;
private A() {
b = new B();
}
public static B getB() {
return b;
}
}
public static B getB() {
return AEnum.bFactory.getB();
}
}
延遲加載(懶漢模式)
延遲加載就是在要首次使用類的時候才進行對象的創建,常見的實現辦法就是在get()或者getInstance()方法中進行new實例化。
public class A {
private static ObjectA a;
private A(){}
public static A getInstance(){
if (a == null) {
a = new A();
}
return a;
}
}
延遲加載的同步問題
延遲加載由於可能出現多個線程同時進入a = new A();
的代碼塊,所以a可能被多次創建,就不符合單例模式的設計,那麼可以通過以下幾個方法進行解決:
同步方法
給獲取實例的方法加鎖,所有進入方法的線程都要排隊進入。但是這種方式效率比較低下,一次只能由一個對象進入,即使在初始化完對象之後。
public synchronized static A getInstance(){}
同步代碼塊
效果等於同步方法
public static A getInstance(){
synchronized(A.class) {
if (a == null) {
a = new A();
}
return a;
}
}
DCL雙檢查鎖機制
這種方式解決了當a初始化完成之後,還需要不斷進入同步代碼塊的情況,當a初始化完成之後,就直接返回對象,不會進入同步塊,同時呢,保證a在初始化過程中,進入同步代碼塊會再判斷一次,避免多個線程同時通過第一層判空的情況。
public static A getInstance(){
if (a == null) {
synchronized(A.class) {
if (a == null) {
a = new A();
}
}
}
return a;
}
線程狀態
NEW
至今尚未啓動的線程,線程剛剛創建完;RUNNABLE
正在JVM中執行的線程,(包含就緒態和執行態,與其他線程輪流使用CPU時間);BLOCKED
被阻塞並等待某個鎖,等待synchronized
或者Lock
;WAITING
調用了wait()方法,等待喚醒,一般是調用了wait()
或join()
方法,等待喚醒或子線程執行完成;TIMED_WAITING
有限時間內等待其他線程的喚醒,一般是調用了wait(long)
、join(long)
、sleep(long)
等等待時間段過去,自動喚醒的線程狀態;IERMINATED
已退出,執行完成,已經退出的線程狀態。
線程組
可以將線程歸屬到某一個線程組中,批量的管理線程或者線程組對象,有效的進行組織。線程組中可以包含線程或者線程組。
使用方法
public class Run {
public static void main(String[] args) {
ThreadA a = new ThreadA();
ThreadB b = new ThreadB();
ThreadGroup group = new ThreadGroup("threadGroupA");
Thread aThread = new Thread(group, a);
Thread bThread = new Thread(group, b);
group.start(); // 批量啓動
// group.interrupt(); // 批量終止
}
}
SimpleDateFormat在多線程條件下不安全
多線程環境中使用SimpleDateFormat比較容易出現時間混亂的情況。
一般有下幾種解決方式:
- 每個時間的解析都new一個新的
SimpleDateFormat
對象,確保線程之間不共享SimpleDateFormat
對象; - 採用ThreadLocal類,確保一個
SimpleDateFormat
對象只在同一時間被一個線程所使用;
線程的異常處理
處理可以通過set異常處理器來解決,優先級別爲:
- Thread對象的
setUncaughtExceptionHandler()
方法; - Thread類的
setDefaultUncaughtExceptionHandler()
方法;