1. 程式人生 > >架構師之路第四天

架構師之路第四天

單例和多執行緒

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