Synchronized和Static Synchronized區別

通過分析這兩個用法的分析,我們可以理解java中鎖的概念。一個是實例鎖(鎖在某一個實例對象上,如果該類是單例,那麼該鎖也具有全局鎖的概念),一個是全局鎖(該鎖針對的是類,無論實例多少個對象,那麼線程都共享該鎖)。實例鎖對應的就是synchronized關鍵字,而類鎖(全局鎖)對應的就是static synchronized(或者是鎖在該類的class或者classloader對象上)。下面的文章做了很好的總結:

 

1.synchronized與static synchronized 的區別
       synchronized是對類的當前實例進行加鎖,防止其他線程同時訪問該類的該實例的所有synchronized塊,注意這裏是“類的當前實例”, 類的兩個不同實例就沒有這種約束了。那麼static synchronized恰好就是要控制類的所有實例的訪問了,static synchronized是限制線程同時訪問jvm中該類的所有實例同時訪問對應的代碼快。實際上,在類中某方法或某代碼塊中有 synchronized,那麼在生成一個該類實例後,改類也就有一個監視快,放置線程併發訪問改實例synchronized保護快,而static synchronized則是所有該類的實例公用一個監視快了,也也就是兩個的區別了,也就是synchronized相當於 this.synchronized,而static synchronized相當於Something.synchronized.


         一個日本作者-結成浩的《java多線程設計模式》有這樣的一個列子:

pulbic class Something(){
    public synchronized void isSyncA(){}
    public synchronized void isSyncB(){}
    public static synchronized void cSyncA(){}
    public static synchronized void cSyncB(){}
}

  那麼,加入有Something類的兩個實例a與b,那麼下列組方法何以被1個以上線程同時訪問呢

a. x.isSyncA()與x.isSyncB() 
b. x.isSyncA()與y.isSyncA()
c. x.cSyncA()與y.cSyncB()
d. x.isSyncA()與Something.cSyncA()

 這裏,很清楚的可以判斷:
a,都是對同一個實例的synchronized域訪問,因此不能被同時訪問
b,是針對不同實例的,因此可以同時被訪問
c,因爲是static synchronized,所以不同實例之間仍然會被限制,相當於Something.isSyncA()與 Something.isSyncB()了,因此不能被同時訪問。
那麼,第d呢?,書上的 答案是可以被同時訪問的,答案理由是synchronzied的是實例方法與synchronzied的類方法由於鎖定(lock)不同的原因。
個人分析也就是synchronized 與static synchronized 相當於兩幫派,各自管各自,相互之間就無約束了,可以被同時訪問。目前還不是分清楚java內部設計synchronzied是怎麼樣實現的。


結論:A: synchronized static是某個類的範圍,synchronized static cSync{}防止多個線程同時訪問這個 類中的synchronized static 方法。它可以對類的所有對象實例起作用。

B: synchronized 是某實例的範圍,synchronized isSync(){}防止多個線程同時訪問這個實例中的synchronized 方法。



2.synchronized方法與synchronized代碼快的區別
        synchronized methods(){} 與synchronized(this){}之間沒有什麼區別,只是
synchronized methods(){} 便於閱讀理解,而synchronized(this){}可以更精確的控制衝突限制訪問區域,有時候表現更高效率。


3.synchronized關鍵字是不能繼承的
        這個在
http://www.learndiary.com/archives/diaries/2910.htm一文中看到的,我想這一點也是很值得注意的,繼承時子類的覆蓋方法必須顯示定義成synchronized。(但是如果使用繼承開發環境的話,會默認加上synchronized關鍵字)

兩種方式效率比較:

1、同步塊,代碼如下:

package Thread.Synchronized;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;



class SynchonizedClass{
	public void start() throws InterruptedException{
		Thread.sleep(100);
		synchronized (this) {
			System.out.println("我運行了 10 ms");
			
		}
	}
}
public class TestSynChronized {

	/**
	 * <p>
	 * </p>
	 * @author zhangjunshuai
	 * @date 2014-6-30 下午2:54:04
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub

		ExecutorService service = Executors.newCachedThreadPool();
		final CountDownLatch cdOrder = new CountDownLatch((1));
		final CountDownLatch cdAnswer = new CountDownLatch(3);
		final SynchonizedClass sc = new SynchonizedClass();
		
		for (int i = 0; i < 3; i++) {
			Runnable runnable = new Runnable(){
				@Override
				public void run() {
					// TODO Auto-generated method stub
					try {
						cdOrder.await();
						sc.start();
						cdAnswer.countDown();
						
					} catch (Exception e) {
						// TODO: handle exception
						e.printStackTrace();
					}
				}
			};
			service.execute(runnable);
		}
		try {
			
			Thread.sleep((long)(Math.random()*10000));
			System.out.println("線程:"+Thread.currentThread().getName()+
					"發佈執行命令");
			cdOrder.countDown();
			long beginTime = System.currentTimeMillis();
			System.out.println("線程:"+Thread.currentThread().getName()+
					"已經發送命令,正在等待結果");
			cdAnswer.await();
			System.out.println( "線程:"
					+Thread.currentThread().getName()+
					"已收到所有響應結果,所用時間爲:"+(System.currentTimeMillis()-beginTime));
			
		} catch (Exception e) {
			// TODO: handle exception
		}
				
	}

}

運行結果如下:

線程main發佈執行命令
線程main已經發送命令,正在等待結果
我運行使用了 10 ms
我運行使用了 10 ms
我運行使用了 10 ms
線程main已收到所有響應結果,所用時間爲:110

 

同步方法,代碼如下:

package Thread.Synchronized;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;



class SynchonizedClass{
	public void start() throws InterruptedException{
		Thread.sleep(100);
		//synchronized (this) {
			System.out.println("我運行了 10 ms");
			
		//}
	}
}
public class TestSynChronized {

	/**
	 * <p>
	 * </p>
	 * @author zhangjunshuai
	 * @date 2014-6-30 下午2:54:04
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub

		ExecutorService service = Executors.newCachedThreadPool();
		final CountDownLatch cdOrder = new CountDownLatch((1));
		final CountDownLatch cdAnswer = new CountDownLatch(3);
		final SynchonizedClass sc = new SynchonizedClass();
		
		for (int i = 0; i < 3; i++) {
			Runnable runnable = new Runnable(){
				@Override
				public void run() {
					// TODO Auto-generated method stub
					try {
						cdOrder.await();
						sc.start();
						cdAnswer.countDown();
						
					} catch (Exception e) {
						// TODO: handle exception
						e.printStackTrace();
					}
				}
			};
			service.execute(runnable);
		}
		try {
			
			Thread.sleep((long)(Math.random()*10000));
			System.out.println("線程:"+Thread.currentThread().getName()+
					"發佈執行命令");
			cdOrder.countDown();
			long beginTime = System.currentTimeMillis();
			System.out.println("線程:"+Thread.currentThread().getName()+
					"已經發送命令,正在等待結果");
			cdAnswer.await();
			System.out.println( "線程:"
					+Thread.currentThread().getName()+
					"已收到所有響應結果,所用時間爲:"+(System.currentTimeMillis()-beginTime));
			
		} catch (Exception e) {
			// TODO: handle exception
		}
				
	}

}

運行結果如下:

線程main發佈執行命令
線程main已經發送命令,正在等待結果
我運行使用了 10 ms
我運行使用了 10 ms
我運行使用了 10 ms
線程main已收到所有響應結果,所用時間爲:332

兩者相差:222ms。


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