java synchronized關鍵字

  

Javasynchronized關鍵字和對象的內置鎖結合使用,用來保護代碼塊在併發環境下的線程安全,可以使被保護的代碼塊操作原子性。

synchronized關鍵字可以用於修飾方法來保護方法內的全部代碼塊,可以用synchronized(對象1) 的方式保護指定代碼塊。(這裏說一下:很多書中都說synchronized可以給對象加鎖,我實在不願意這麼說,這樣讓我概念混淆。。。因爲,對象內置鎖是本來就存在的,不是誰加給它的,“synchronized(對象1)”我更願意解釋成:執行到此的線程需要去獲取到(持有)“對象1”的對象鎖才能執行synchronized塊中的代碼)。

而多個線程在執行某一被synchronized保護的代碼或者方法時,能夠進行互斥的關鍵在於synchronized的是不是同一個鎖。下面是synchronized使用的幾種情況並分析他分別持有的是哪一個對象鎖(假設這些方法都屬於 Salary):

1、  synchronized修飾非靜態方法:持有Salary類實例的對象鎖

2、  synchronized修飾靜態方法:持有Salary.class的對象鎖

3、  synchronized(對象1):持有對象1 的對象鎖

4、  synchronized(this):持有Salary類當前對象實例的對象鎖

5、  synchronized(Salary.class): 持有Salary.class的對象鎖

下面用幾個程序實驗一下。

一、synchronized修飾非靜態方法:持有Salary類實例的對象鎖

併發對象類Salary:

public class Salary {
	public synchronized void method1(){
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + " has entered method1 at:"+System.currentTimeMillis()/1000);
	}
	
	public void  method2(){
		System.out.println(Thread.currentThread().getName() + " has entered method2 at:"+System.currentTimeMillis()/1000);
	}
}

併發程序SynchronizeTest:

public class SynchronizeTest {

	public static void main(String[] args) {
		final Salary salary = new Salary();
		Thread t1 = new Thread(new Runnable() {			
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000);
				salary.method1();
				System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000);
			}
		});
		t1.start();
		
		//爲了保證線程t1首先獲得對象鎖,在此處主線程掛起1秒鐘,再執行後面的啓動線程t2的代碼。
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		//如果線程t2在啓動4秒之後才進入salary.method2(),則說明請求的是同一個對象鎖。否則,就不是同一個對象鎖
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000);
				synchronized (salary) { //TODO1
					salary.method2();
				}			
				System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000);
			}
		});
		t2.start();
	}
}

運行結果:

Thread-0 start at:1452076058

Thread-1 start at:1452076059

Thread-0 has entered method1 at:1452076063

Thread-0 end at:1452076063

Thread-1 has entered method2 at:1452076063

Thread-1 end at:1452076063


從運行結果可以看出,當線程t1進入方法method1 5秒之後(釋放了對象鎖),線程t2才進入方法method2,說明兩個線程持有的是相同的對象鎖。


二、synchronized修飾靜態方法:持有Salary.class的對象鎖

併發對象類Salary

public class Salary {
	public synchronized static void method1(){
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + " has entered method1 at:"+System.currentTimeMillis()/1000);
	}
	
	public void method2(){
		System.out.println(Thread.currentThread().getName() + " has entered method2 at:"+System.currentTimeMillis()/1000);
	}
}

併發程序SynchronizeTest

public class SynchronizeTest {

	public static void main(String[] args) {
		final Salary salary = new Salary();
		Thread t1 = new Thread(new Runnable() {			
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000);
				Salary.method1();
				System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000);
			}
		});
		t1.start();
		
		//爲了保證線程t1首先獲得對象鎖,在此處主線程掛起1秒鐘,再執行後面的啓動線程t2的代碼。
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		//如果線程t2在啓動4秒之後才進入salary.method2(),則說明請求的是同一個對象鎖。否則,就不是同一個對象鎖
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000);
				synchronized (Salary.class) {
					salary.method2();
				}			
				System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000);
			}
		});
		t2.start();
	}
}

運行結果:

Thread-0 start at:1452076058

Thread-1 start at:1452076059

Thread-0 has entered method1 at:1452076063

Thread-0 end at:1452076063

Thread-1 has entered method2 at:1452076063

Thread-1 end at:1452076063


三、synchronized(對象1):持有對象的對象鎖

併發對象類Salary:

public class Salary {
	private Object lock = new Object();
	public Salary(Object lock){
		this.lock = lock;
	}
	public void method1(){
		synchronized (lock) {
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + " has entered method1 at:"+System.currentTimeMillis()/1000);
		}
	}
	
	public void  method2(){
		System.out.println(Thread.currentThread().getName() + " has entered method2 at:"+System.currentTimeMillis()/1000);
	}
}

併發程序SynchronizeTest:

public class SynchronizeTest {

	public static void main(String[] args) {
		final Object 對象1 = new Object();
		final Salary salary = new Salary(對象1);
		Thread t1 = new Thread(new Runnable() {			
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000);
				salary.method1();
				System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000);
			}
		});
		t1.start();
		
		//爲了保證線程t1首先獲得對象鎖,在此處主線程掛起1秒鐘,再執行後面的啓動線程t2的代碼。
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		//如果線程t2在啓動4秒之後才進入salary.method2(),則說明請求的是同一個對象鎖。否則,就不是同一個對象鎖
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000);
				synchronized (對象1) {
					salary.method2();
				}			
				System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000);
			}
		});
		t2.start();
	}
}

運行結果:

Thread-0 start at:1452077315

Thread-1 start at:1452077316

Thread-0 has entered method1 at:1452077320

Thread-1 has entered method2 at:1452077320

Thread-1 end at:1452077320

Thread-0 end at:1452077320


四、  synchronized(this):持有Salary類當前對象實例的對象鎖

併發對象類Salary:

public class Salary {
	private Object lock = new Object();
	public Salary(Object lock){
		this.lock = lock;
	}
	public void method1(){
		synchronized (this) {
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + " has entered method1 at:"+System.currentTimeMillis()/1000);
		}
	}
	
	public void method2(){
		System.out.println(Thread.currentThread().getName() + " has entered method2 at:"+System.currentTimeMillis()/1000);
	}
}


併發程序SynchronizeTest:

public class SynchronizeTest {

	public static void main(String[] args) {
		final Object 對象1 = new Object();
		final Salary salary = new Salary(對象1);
		Thread t1 = new Thread(new Runnable() {			
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000);
				salary.method1();
				System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000);
			}
		});
		t1.start();
		
		//爲了保證線程t1首先獲得對象鎖,在此處主線程掛起1秒鐘,再執行後面的啓動線程t2的代碼。
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		//如果線程t2在啓動4秒之後才進入salary.method2(),則說明請求的是同一個對象鎖。否則,就不是同一個對象鎖
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000);
				synchronized (salary) {
					salary.method2();
				}			
				System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000);
			}
		});
		t2.start();
	}
}

運行結果:

Thread-0 start at:1452077439

Thread-1 start at:1452077440

Thread-0 has entered method1 at:1452077444

Thread-0 end at:1452077444

Thread-1 has entered method2 at:1452077444

Thread-1 end at:1452077444


五、  synchronized(Salary.class): 持有Salary.class的對象鎖

與測試程序二同理。


總結一下,synchronized的使用變種可能還有很多,但是萬變不離其宗,分析多線程是否在synchronized處產生了互斥,其根本就是要分析synchronized所持有的對象鎖是否爲同一個對象鎖。這很關鍵!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章