架構師之路第四天
阿新 • • 發佈:2018-12-17
單例和多執行緒
ThreadLocal概念:執行緒區域性變數,是一個多執行緒間併發訪問變數的解決方案。與其synchronized等加鎖的方式不同,ThreadLocal完全不提供鎖,而使用空間換時間的手段,為每個執行緒提供變數的獨立副本,以保障執行緒的安全。
從效能上說,ThreadLocal不具備絕對的優勢,在併發不是很高的時候,加鎖的效能會更好,但作為一套與鎖完全無關的執行緒安全解決方案,在高併發或者競爭激烈的場景,使用ThreadLocal可以在一定的程度上減少鎖競爭。
ThreadLocl的程式碼案例如下:
public class Test4 { private ThreadLocal<String> list = new ThreadLocal<String>(); public void setVlaue(String value) { list.set(value); } public void getValue() { System.out.println("當前的執行緒:"+Thread.currentThread().getName()+";值為:"+list.get()); } public static void main(String[] args) throws Exception { Test4 test1 = new Test4(); Thread t1 = new Thread(new Runnable() { @Override public void run() { try { test1.setVlaue("a"); test1.getValue(); } catch (Exception e) { e.printStackTrace(); } } },"t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000); test1.getValue(); } catch (Exception e) { e.printStackTrace(); } } },"t2"); t1.start(); t2.start(); } }
執行的結果是:
當前的執行緒:t1;值為:a
當前的執行緒:t2;值為:null
解釋:ThreadLocal完全不提供鎖,而使用空間換時間的手段,為每個執行緒提供變數的獨立副本。也就是執行緒間的ThreadLocal是不可見的,互相不影響的。
單例與多執行緒
單例模式最常見的就是懶漢式和餓漢式,名字的命名就是依據與建立物件的先後順序。其中在實際的開發中使用的單例模式有兩種:一是靜態內部類,二是懶漢式的雙重判斷。
靜態內部類:
public class Test5 { // 靜態內部類 private static class Singletion{ private static Singletion singletion = new Singletion(); } // 獲取例項 public static Singletion getInstance() { return Singletion.singletion; } }
懶漢式的雙重判斷(只有一重判斷的案例):
public class Test6 { // 靜態內部類 private static Test6 test = null; public static Test6 getInstance() { if (test == null) { try { // 模擬業務場景 Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } synchronized (Test6.class) { //if (test == null) { test = new Test6(); //} } } return test; } public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { try { System.out.println(Test6.getInstance().hashCode()); } catch (Exception e) { e.printStackTrace(); } } }, "t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { try { System.out.println(Test6.getInstance().hashCode()); } catch (Exception e) { e.printStackTrace(); } } }, "t2"); Thread t3 = new Thread(new Runnable() { @Override public void run() { try { System.out.println(Test6.getInstance().hashCode()); } catch (Exception e) { e.printStackTrace(); } } }, "t3"); t1.start(); t2.start(); t3.start(); } }
輸出的結果:
1384277657
1804397488
619028145
懶漢式的雙重判斷(雙重判斷的案例):
public class Test6 {
// 靜態內部類
private static Test6 test = null;
public static Test6 getInstance() {
if (test == null) {
try {
// 模擬業務場景
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (Test6.class) {
// 這裡需要判斷非空
if (test == null) {
test = new Test6();
}
}
}
return test;
}
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try { System.out.println(Test6.getInstance().hashCode());
} catch (Exception e) {
e.printStackTrace();
}
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try { System.out.println(Test6.getInstance().hashCode());
} catch (Exception e) {
e.printStackTrace();
}
}
}, "t2");
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
try { System.out.println(Test6.getInstance().hashCode());
} catch (Exception e) {
e.printStackTrace();
}
}
}, "t3");
t1.start();
t2.start();
t3.start();
}
}
輸出的結果是:
1384277657
1384277657
1384277657