1. 程式人生 > >Java中synchronized關鍵字理解

Java中synchronized關鍵字理解

監視器 pre 定義 exc 執行 zed 三種 gen 好記性不如爛筆頭

好記性不如爛筆頭~~

並發編程中synchronized關鍵字的地位很重要,很多人都稱它為重量級鎖。利用synchronized實現同步的基礎:Java中每一個對象都可以作為鎖。具體表現為以下三種形式。

(1)對於普通同步方法,鎖是當前實例對象。

(2)對於靜態同步方法,鎖是當前類的Class對象。

(3)對於同步方法塊,鎖是synchronized括號裏配置的對象。

一、普通同步方法

使用synchronized關鍵字修飾一個普通方法,鎖住的是當前實例的對象。當synchronized鎖住該對象後,別的線程如果也想拿到這個對象的鎖,就必須等待這個線程執行完成釋放鎖,才能再次給對象加鎖,這樣才達到線程同步的目的。

實驗一

class Synch{
	public synchronized  void test1(){
		System.out.println("test1開始");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("test1結束");
	}
	public synchronized void test2(){
		System.out.println("test2開始");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("test2結束");
	}
}

public class SyncTest extends Synch{
	
   public static void main(String args[]){
	   Synch s=new Synch();
	   Thread t1=new Thread(new Runnable(){

		@Override
		public void run() {
			// TODO Auto-generated method stub
			s.test1();
		}
		   
	   });
	   Thread t2=new Thread(new Runnable(){

		@Override
		public void run() {
			// TODO Auto-generated method stub
			s.test2();
		}});
	   t1.start();
	   t2.start();
   }
}

  實驗結果:技術分享

分析上述代碼,類Synch中有兩個普通同步方法test1和test2.在主函數中實現了該類,同時定義兩個thread線程,run方法中分別調用類Synch的方法。synchronized實現的普通同步方法,鎖住的是當前實例對象,即Synch類對象。由於兩個線程是調用的同一個對象中的同步方法,所以只有一個線程釋放該對象的鎖,另一個線程才能調用。

實驗二

class Sync{
	public synchronized void test(String threadname){
		
		System.out.println(threadname+"開始");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(threadname+"結束");

	}
}
class MyThread extends Thread{
	public int i;
	public MyThread(int i){
		this.i=i;
	}
	Sync s=new Sync();
	public void run(){		
		//Sync sync=new Sync();
		s.test("Thread"+i);
	}
}
public class SynTest {
    public static void main(String args[]){
    	for(int i=0;i<3;i++){
			Thread thread=new MyThread(i);
			thread.start();
		}
    }
}

 實驗結果:技術分享

上述代碼,每個線程中都new了一個Sync類的對象,也就是產生了三個Sync對象,由於不是同一個對象,所以可以多線程同時運行synchronized方法。

二、靜態同步方法

對於靜態同步方法,鎖是當前類的Class對象,所以,static synchronized方法也相當於全局鎖,相當於鎖住了代碼段。只有一個線程結束後,另一個線程才能獲得鎖。

實驗三

class Synch{
	public static synchronized  void test1(){
		System.out.println("test1開始");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("test1結束");
	}
	public static synchronized  void test2(){
		System.out.println("test2開始");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("test2結束");
	}
}

public class SyncTest extends Synch{
	
   public static void main(String args[]){
	   Synch s1=new Synch();
	   Synch s2=new Synch();
	   Thread t1=new Thread(new Runnable(){

		@Override
		public void run() {
			// TODO Auto-generated method stub
			s1.test1();
		}
		   
	   });
	   Thread t2=new Thread(new Runnable(){

		@Override
		public void run() {
			// TODO Auto-generated method stub
			s2.test2();
		}});
	   t1.start();
	   t2.start();
   }
}

  實驗結果:技術分享

三、同步方法塊

這部分更好理解,鎖住的是synchronized括號裏配置的對象。

實驗四

public class Threadtest implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		synchronized(this){
			  for (int i = 0; i < 5; i++) {  
                  System.out.println(Thread.currentThread().getName() + " synchronized loop " + i);  
             }  
		}
	}
	public static void main(String[] args) {  
        Threadtest t1 = new Threadtest();  
        Thread ta = new Thread(t1, "A");  
        Thread tb = new Thread(t1, "B");  
        ta.start();  
        tb.start();  
   } 
}

運行結果:技術分享

上述代碼,synchronized代碼塊括號裏配置的對象是this,同一個對象,所以只有一個線程訪問該代碼塊結束後,釋放鎖,另一個線程才能訪問該代碼塊。

需要註意的是,其他線程可以訪問非synchronized(this)同步代碼。如下代碼

實驗五

class MyThread1{
	public void test1(){
    	synchronized(this){
    		System.out.println("同步代碼塊-test1開始");
    		try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
    		System.out.println("同步代碼塊-test1結束");
    	}
    }
    public void test2(){
    	try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	System.out.println("非同步代碼塊-test2");
    }
}
public class ThreadTest1 {
	public static void main(String args[]){
    MyThread1 t=new MyThread1();
    Thread t1=new Thread(new Runnable(){

		@Override
		public void run() {
			// TODO Auto-generated method stub
			t.test1();
		}
    	
    });
    Thread t2=new Thread(new Runnable(){

		@Override
		public void run() {
			// TODO Auto-generated method stub
			t.test2();
			
		}
    	
    });
    t1.start();
    t2.start();
	}
}

  運行結果:技術分享

synchronized括號裏配置的是this,只是對這一段代碼進行了加鎖,同一個對象,只有一個線程能訪問該代碼塊,釋放鎖之後,其他線程才可以訪問,但是並不影響其他線程訪問非同步代碼塊。

同步代碼塊,同步方法的實現~~簡單理解

對於同步代碼塊是使用monitorenter、monitorexit指令實現的。每個對象都有一個監視器鎖(monitor),當monitor被占用時,就會處於鎖定狀態。

線程執行monitorenter指令嘗試獲取monitor的所有權。

  • 如果monitor的進入數為0,則該線程進入monitor,並將進入數設置為1
  • 如果該線程已經占有了該monitor,重新進入,進入數也要+1
  • 如果其他線程占用了monitor,則該線程進入阻塞狀態,知道進入數為0

對於同步方法,常量池中多了ACC_SYNCHRONIZED標識符,方法調用時,先檢查該標識符的訪問標誌,如果設置了,則進程先獲取monitor,獲取成功後才能執行方法體,方法執行完之後再釋放monitor。

整理下,要不會忘記~~參考http://www.cnblogs.com/QQParadise/articles/5059824.html

Java中synchronized關鍵字理解