1. 程式人生 > >多執行緒(2)-synchronized方法和synchronized程式碼塊的用法

多執行緒(2)-synchronized方法和synchronized程式碼塊的用法

 

前言

       在介紹synchronized方法和synchronized程式碼塊前,先對監視器(Monitor)做一個說明,在java虛擬機器中,每個物件(object和class)通過某種邏輯關聯監視器,每個監視器和一個物件引用相關聯,為了實現監視器的互斥功能,每個物件都關聯著一把鎖一旦方法或者程式碼塊被synchronized修飾,那麼這個部分就放入了監視器的監視區域,確保一次只能有一個執行緒執行該部分的程式碼,執行緒在獲取鎖之前不允許執行該部分的程式碼。

       想詳細瞭解此概念可以檢視下面的部落格:

       JAVA併發:多執行緒程式設計之同步“監視器monitor”(三)

synchronized方法

      synchronized方法分為兩種,一種是靜態synchronized方法,另一種是非靜態synchronized方法。這兩種的區別在於兩個的物件鎖不同及監視器不同。非靜態synchronized方法的鎖是當前例項物件。如果是同一個例項物件,那麼不同執行緒呼叫同一非靜態synchronized方法與呼叫不同非靜態synchronized方法都競爭同一把鎖,如果是不同例項物件,那麼就會非同步執行。下面以一個例子展示下同一個例項物件呼叫同一個非靜態synchronized方法。

package thread.synchronize;

/***
 * 需要同步的邏輯處理層
 * @author swh
 *
 */
public class SynchronizedMethondService {

	synchronized public void doService() {
		System.out.println(Thread.currentThread().getName()+":"+"開始執行");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+":"+"執行結束");
	}
	
	synchronized public void doServiceB() {
		System.out.println(Thread.currentThread().getName()+":"+"B開始執行");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+":"+"B執行結束");
	}
}
package thread.synchronize;

/***
 * 用非靜態synchronized方法的測試執行緒
 * @author swh
 *
 */
public class SynchronizedMethondThread extends Thread {
	private SynchronizedMethondService service;
    public SynchronizedMethondThread(SynchronizedMethondService service) {
    	this.service = service;
    }
	@Override
	public void run() {
		service.doService();
	}
	
	public static void main(String args[]) {
		SynchronizedMethondService service = new SynchronizedMethondService();
		SynchronizedMethondThread  threadA = new SynchronizedMethondThread(service);
		threadA.start();
		SynchronizedMethondThread  threadB = new SynchronizedMethondThread(service);
		threadB.start();
	}
}

    下面的執行的結果,因為呼叫的通過一個例項物件的非靜態synchronized方法,需要競爭同一把鎖,所以會同步執行。我現在呼叫不同例項物件的方法看下是否會非同步執行。更改的程式碼如下:

package thread.synchronize;

/***
 * 用非synchronized方法的測試執行緒
 * @author swh
 *
 */
public class SynchronizedMethondThread extends Thread {
	private SynchronizedMethondService service;
    public SynchronizedMethondThread(SynchronizedMethondService service) {
    	this.service = service;
    }
	@Override
	public void run() {
		service.doService();
	}
	
	public static void main(String args[]) {
		SynchronizedMethondService serviceA = new SynchronizedMethondService();
		SynchronizedMethondThread  threadA = new SynchronizedMethondThread(serviceA);
		threadA.start();
		SynchronizedMethondService serviceB = new SynchronizedMethondService();
		SynchronizedMethondThread  threadB = new SynchronizedMethondThread(serviceB);
		threadB.start();
	}
}

最後執行的結果如下,因為兩個執行緒執行的方法是兩個不同例項物件的,兩個執行緒競爭的不是同一把鎖,所以最後非同步執行了。

     上面的例子我們驗證了不同執行緒呼叫同一例項物件的非靜態synchronized方法和不同例項物件的步同非靜態synchronized方法,下面我們再驗證下不同執行緒呼叫不同非靜態synchronized方法。

package thread.synchronize;
/***
 * 用非synchronized方法的測試執行緒
 * @author swh
 *
 */
public class SynchronizedMethondThreadB extends Thread {

	private SynchronizedMethondService service;
    public SynchronizedMethondThreadB(SynchronizedMethondService service) {
    	this.service = service;
    }
	@Override
	public void run() {
		service.doServiceB();
	}
}
package thread.synchronize;

/***
 * 用非synchronized方法的測試執行緒
 * @author swh
 *
 */
public class SynchronizedMethondThread extends Thread {
	private SynchronizedMethondService service;
    public SynchronizedMethondThread(SynchronizedMethondService service) {
    	this.service = service;
    }
	@Override
	public void run() {
		service.doService();
	}
	
	public static void main(String args[]) {
		SynchronizedMethondService serviceA = new SynchronizedMethondService();
		SynchronizedMethondThread  threadA = new SynchronizedMethondThread(serviceA);
		threadA.start();
		SynchronizedMethondThreadB  threadB = new SynchronizedMethondThreadB(serviceA);
		threadB.start();
	}
}

   

  靜態synchronized方法和非靜態synchronized方法方法區別在於靜態synchronized方法的鎖是當前類的class物件,而非靜態synchronized方法的鎖是當前例項物件,所有靜態synchronized方法競爭的都是同一把鎖,下面我們以一個簡單的例子來驗證下。

package thread.synchronize;

/***
 * 需要同步的邏輯處理層
 * @author swh
 *
 */
public class SynchronizedMethondService {

	synchronized public static void doService() {
		System.out.println(Thread.currentThread().getName()+":"+"開始執行");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+":"+"執行結束");
	}
	
	synchronized public static void doServiceB() {
		System.out.println(Thread.currentThread().getName()+":"+"B開始執行");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+":"+"B執行結束");
	}
}
package thread.synchronize;

/***
 * synchronized方法的測試執行緒
 * @author swh
 *
 */
public class SynchronizedMethondThread extends Thread {
	private SynchronizedMethondService service;
    public SynchronizedMethondThread(SynchronizedMethondService service) {
    	this.service = service;
    }
	@Override
	public void run() {
		service.doService();
	}
	
	public static void main(String args[]) {
		SynchronizedMethondService serviceA = new SynchronizedMethondService();
		SynchronizedMethondThread  threadA = new SynchronizedMethondThread(serviceA);
		threadA.start();
		SynchronizedMethondService serviceB = new SynchronizedMethondService();
		SynchronizedMethondThread  threadB = new SynchronizedMethondThread(serviceB);
		threadB.start();
	}
}

執行的結果如下,是同步執行的。

synchronized程式碼塊

      同步程式碼塊需要傳遞的物件(鎖物件):就是鎖住這個物件,表示這個物件正在為我服務,其他執行緒不能用(非synchronized程式碼塊、方法除外),下面我驗證呼叫同一個例項物件的同步方法同步程式碼是否會同步執行。

package thread.synchronize;

/***
 * 需要同步的邏輯處理層
 * @author swh
 *
 */
public class SynchronizedMethondService {

	synchronized public  void doService() {
		System.out.println(Thread.currentThread().getName()+":"+"開始執行");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+":"+"執行結束");
	}
	
	synchronized public  void doServiceB() {
		synchronized(this){
			System.out.println(Thread.currentThread().getName()+":"+"B開始執行");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+":"+"B執行結束");
		}
	
	}
}
package thread.synchronize;
/***
 * 用synchronize程式碼塊的測試執行緒
 * @author swh
 *
 */
public class SynchronizedMethondThreadB extends Thread {

	private SynchronizedMethondService service;
    public SynchronizedMethondThreadB(SynchronizedMethondService service) {
    	this.service = service;
    }
	@Override
	public void run() {
		service.doServiceB();
	}
}
package thread.synchronize;

/***
 * synchronized方法的測試執行緒
 * @author swh
 *
 */
public class SynchronizedMethondThread extends Thread {
	private SynchronizedMethondService service;
    public SynchronizedMethondThread(SynchronizedMethondService service) {
    	this.service = service;
    }
	@Override
	public void run() {
		service.doService();
	}
	
	public static void main(String args[]) {
		SynchronizedMethondService serviceA = new SynchronizedMethondService();
		SynchronizedMethondThread  threadA = new SynchronizedMethondThread(serviceA);
		threadA.start();
		SynchronizedMethondThreadB  threadB = new SynchronizedMethondThreadB(serviceA);
		threadB.start();
	}
}