1. 程式人生 > >synchronized的修飾方法和修飾程式碼塊區別

synchronized的修飾方法和修飾程式碼塊區別

文章思路

  • 哪些概念難理解
    • 類鎖和物件鎖區別
      • 類鎖所有物件一把鎖
      • 物件鎖一個物件一把鎖,多個物件多把鎖
    • 同步是對同一把鎖而言的,同步這個概念是在多個執行緒爭奪同一把鎖的時候才能實現的,如果多個執行緒爭奪不同的鎖,那多個執行緒是不能同步的
      • 兩個執行緒一個取物件鎖,一個取類鎖,則不能同步
      • 兩個執行緒一個取a物件鎖,一個取b物件鎖,則不能同步
  • 文章關鍵內容:
    • 什麼是鎖
    • 鎖有幾種
    • 什麼是synchronized
    • synchronized用法
      • 用於方法
        • 得到類鎖
        • 得到物件鎖
      • 用與程式碼塊
        • 得到類鎖
        • 得到物件鎖
    • 兩種鎖的使用

synchronized作用

synchronized是用來完成多執行緒條件下同步工作的

若沒有同步,一個方法有多條語句,兩個執行緒A和B都要都用某個方法,而A執行緒在呼叫這個方法的時候,B執行緒可不會等它多條語句都執行完再去呼叫這個方法,更有可能的是A執行緒沒有執行完這個方法,B執行緒奪得CPU執行權,A對這個方法的呼叫就停止在它最後執行到的語句位置,直到A下一次搶到CPU執行權A才能繼續執行它未執行完的程式碼。

有了同步後,當A獲得執行權並開始執行此方法,B就必須乖乖等A將方法全部執行完之後才有權去執行此方法。

鎖和synchronized的關係

鎖是Java中用來實現同步的工具
而之所以能對方法或者程式碼塊實現同步的原因就是
只有拿到鎖的執行緒才能執行synchronized修飾的方法或程式碼塊,且其他執行緒獲得鎖的唯一方法是等目前拿到鎖的執行緒執行完方法將鎖釋放,如果synchronized修飾的程式碼塊或方法沒執行完是不會釋放這把鎖的,這就保證了拿到鎖的執行緒一定可以一次性把它呼叫的方法或程式碼塊執行完

synchronized有幾種用法

用於方法

class B {
	//在方法前面加上synchronized關鍵字表示作用於該方法
	//需要注意方法有兩種,一種靜態方法,一種非靜態方法
	//兩者區別在於,當修飾靜態時候,大家都呼叫的是同一個。當修飾非靜態方法時候,呼叫的是每個物件自己的那個方法,因為非靜態域或方法是每個物件各自都有一份的,靜態方法是所有物件公有的。
	synchronized public static void mB(String value) throws InterruptedException {
		for (int i = 0; i < 1000; i++) {
			System.out.print(value);
		}
	}
	synchronized public void mC(String value) throws InterruptedException {
		for (int i = 0; i < 1000; i++) {
			System.out.print(value);
		}
	}	
}

用於程式碼塊


class A {
	public static void test() {
		//修飾程式碼塊的情況也有兩種,這裡表示對類進行同步
		synchronized (A.class) {
			System.out.println("haha");
		}
	}
	public void test2() {
		//這裡表示對當前物件進行同步,兩者區別看下面鎖有幾種
		synchronized (this) {
			System.out.println("haha");
		}
	}
}

鎖有幾種

類鎖
類鎖,是用來鎖類的,我們知道一個類的所有物件共享一個class物件,共享一組靜態方法,類鎖的作用就是使持有者可以同步地呼叫靜態方法。當synchronized指定修飾靜態方法或者class物件的時候,拿到的就是類鎖,類鎖是所有物件共同爭搶一把。

//B中有兩個方法mB和mC
//mB是synchronized修飾靜態方法,拿到類鎖
//mC是synchronized修飾非靜態方法,拿到的也是類鎖
class B {
	synchronized public static void mB(String value) throws InterruptedException {
		for (int i = 0; i < 1000; i++) {
			System.out.print(value);
		}
	}

	public void mC(String value) {
		synchronized (B.class) {
			for (int i = 0; i < 1000; i++) {
				System.out.print(value);
			}
		}
	}
}

物件鎖
物件鎖,是用來物件的,虛擬機器為每個的非靜態方法和非靜態域都分配了自己的空間,不像靜態方法和靜態域,是所有物件共用一組。
所以synchronized修飾非靜態方法或者this的時候拿到的就是物件鎖,物件鎖是每個物件各有一把的,即同一個類如果有兩個物件。

//類C中有兩個方法mB和mC
//mB是synchronized非靜態方法,拿到物件鎖
//mC是synchronized修飾this,拿到的也是物件鎖
class C {
	synchronized publi void mB(String value) throws InterruptedException {
		for (int i = 0; i < 1000; i++) {
			System.out.print(value);
		}
	}

	public void mC(String value) {
		synchronized (this) {
			for (int i = 0; i < 1000; i++) {
				System.out.print(value);
			}
		}
	}
}

物件鎖和類鎖的使用

物件鎖
下例中,兩個執行緒呼叫同一個物件b的mB方法。最終結果是輸出了1000次“1”之後輸出了1000次“2”。可見兩個執行緒對此方法的呼叫實現了同步。

class B {
	//修飾非靜態方法拿到物件鎖
	synchronized public void mB(String name) throws InterruptedException {
		for (int i = 0; i < 1000; i++) {
			System.out.print(name);
		}
	}
	//修飾this拿到物件鎖
	public void mB2(String name) throws InterruptedException {
		synchronized(this) {
			for (int i = 0; i < 1000; i++) {
				System.out.print(name);
			}
		}
	}
}

public class test {
	
	public static void main(String[] args) {

		B b = new B();
		Thread thread1 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					//執行緒1的呼叫處
					b.mB("1");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
		Thread thread2 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					//執行緒2的呼叫處
					b.mB2("2");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
		thread1.start();
		thread2.start();
	}
}

類鎖
下面程式碼中對靜態方法mA和mC的呼叫實現了同步,結果沒有交替輸出1和2,而是一次性輸出完成後再輸出的另一種

class B {
	//修飾靜態方法,呼叫取得類鎖
	synchronized public static void mB(String value) throws InterruptedException {
		for (int i = 0; i < 1000; i++) {
			System.out.print(value);
		}
	}
	//修飾class物件,呼叫取得靜類鎖
	public static void mC(String value) {
		synchronized (B.class) {
			for (int i = 0; i < 1000; i++) {
				System.out.print(value);
			}
		}
	}

	public static void main(String[] args) {

		Thread thread = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					B.mB("1");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

			}
		});
		Thread thread2 = new Thread(new Runnable() {
			@Override
			public void run() {
				B.mC("2");
			}
		});
	}
}

類鎖和物件鎖同時存在
同時存在的情況下,兩者做不到同步。類鎖和物件鎖是兩種鎖。下述情況,類B的靜態方法和程式碼塊功能都是列印100個value值,但是靜態方法是類鎖,而程式碼塊鎖this,是物件鎖。所以程式碼塊和靜態方法交替執行、交替列印,大家可複製程式碼自行驗證。

class B {
	//靜態方法,上類鎖,函式功能為連續列印1000個value值,呼叫時會傳1,所以會列印1000個1
	synchronized public static void mB(String value) throws InterruptedException {
		for (int i = 0; i < 1000; i++) {
			System.out.print(value);
		}
	}
	
	public void mC(String value) {
		//修飾this上物件鎖,函式功能也是連續列印1000個value值,呼叫時會傳2,所以會列印1000個2
		synchronized (this) {
			for (int i = 0; i < 1000; i++) {
				System.out.print(value);
			}
		}
	}

	public static void main(String[] args) {

		Thread thread = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					B.mB("1");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

			}
		});
		Thread thread2 = new Thread(new Runnable() {
			@Override
			public void run() {
				new B().mC("2");
			}
		});
		thread.start();
		thread2.start();
		
	}
}