1. 程式人生 > 程式設計 >淺析Java 併發程式設計中的synchronized

淺析Java 併發程式設計中的synchronized

synchronized關鍵字,我們一般稱之為“同步鎖”,用它來修飾需要同步的方法和需要同步程式碼塊,預設是當前物件作為鎖的物件。在用synchronized修飾類時(或者修飾靜態方法),預設是當前類的Class物件作為鎖的物件,故存在著方法鎖、物件鎖、類鎖這樣的概念。

一、沒有設定執行緒同步的情況

先給出以下程式碼感受下程式碼執行的時候為什麼需要同步?程式碼可能比較枯燥,配上業務理解起來就會舒服很多,學生軍訓,有三列,每列5人,需要報數,每個執行緒負責每一列報數。

class SynchronizedExample {
	protected static int num = 0;
	protected void numberOff() {
		for(int i=0; i<5; i++) {
			num++;
System.out.println(Thread.currentThread().getName()+":"+SynchronizedExample.num);
		}
	}
}

public class SynchronizedTest {
	public static void main(String[] args) throws InterruptedException {
		SynchronizedExample se = new SynchronizedExample();
		for(int i=1; i<=3; i++) {
			new Thread( ()-> {se.numberOff();},"執行緒"+i).start();
		}
	}
}

執行結果如下:

執行緒1:1
執行緒2:2
執行緒1:3
執行緒3:4
.......

之所以出現這種情況,是因為三個執行緒是非同步的,沒有同步。
對應的業務場景就是,在第一列沒有完成報數的時候,其他佇列搶報了,這在現實中是不允許的,所以需要類似於synchronized等具有同步功能的關鍵字粉末登場。

二、方法同步鎖

當報數方法加上synchronized關鍵字之後,就會一列一列的報數。

protected synchronized void numberOff() {
	for(int i=0; i<5; i++) {
		num++;
System.out.println(Thread.currentThread().getName()+":"+SynchronizedExample.num);
	}
}

執行結果如下:

執行緒1:1
執行緒1:2
執行緒1:3
執行緒1:4
......

寫到這裡還是要從技術層面講下原理,當一個執行緒執行帶有synchronized關鍵字的方法時,該執行緒會在該方法處設定一個鎖(其他執行緒打不開這個鎖,只能在外邊等該執行緒釋放掉該鎖,一般都是執行完所有程式碼後主動釋放鎖),表示此方法是當前執行緒獨佔的,對應到上述業務中就是一次只能有一個佇列報數。

三、物件鎖

改進後的程式碼用到了一個物件鎖,該物件鎖預設是當前物件,上述程式碼等同於以下程式碼:

protected void numberOff() {
	synchronized (this) {
		for (int i = 0; i < 5; i++) {
			num++;
			System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num);
		}
	}
}

當多個執行緒用一個物件鎖,各個執行緒可以達到同步的作用,如果每個執行緒都用自己的物件鎖,那麼synchronized就失去了同步的作用。如以下程式碼:

class SynchronizedExample {
	protected static int num = 0;
	protected void numberOff() {
		synchronized (this) {
			for (int i = 0; i < 5; i++) {
				num++;
			System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num);
			}
		}
	}
}
public class SynchronizedTest {
	public static void main(String[] args) throws InterruptedException {
		SynchronizedExample se = new SynchronizedExample();
		for(int i=1; i<=3; i++) {
			new Thread( ()-> {new SynchronizedExample().numberOff();},"佇列"+i).start();
		}
	}
}

執行結果如下:

執行緒1:1
執行緒2:2
執行緒1:3
執行緒3:4
.......

有讀者會說不同執行緒執行的是不同物件中的方法,肯定達不到同步的效果,也對,也很有道理,接著看如下程式碼:

class SynchronizedExample {
	protected static int num = 0;
	protected void numberOff(Object lock) {
		synchronized (lock) {
			for (int i = 0; i < 5; i++) {
				num++;
			System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num);
			}
		}
	}
}

public class SynchronizedTest {
	public static void main(String[] args) throws InterruptedException {
		SynchronizedExample se = new SynchronizedExample();
		for(int i=1; i<=3; i++) {
			new Thread( ()-> {se.numberOff(new Object());},"佇列"+i).start();
		}
	}
}

執行結果如下:

執行緒1:1
執行緒2:2
執行緒1:3
執行緒3:4
.......

四、類鎖

對於上述問題,讀者應該得出一個結論,要想達到同步的效果,必須用同一個鎖,此時類鎖可以粉末登場。看如下程式碼:

protected void numberOff(Object lock) {
	synchronized (SynchronizedExample.class) {
		for (int i = 0; i < 5; i++) {
			num++;
			System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num);
		}
	}
}

上述程式碼可以達到同步的效果。

五、靜態鎖

靜態鎖是針對靜態方法而言,當一個靜態方法中有synchronized關鍵字時,預設的是使用當前類位元組碼物件作為鎖。程式碼示例如下:

class SynchronizedExample {
	protected static int num = 0;
	protected synchronized static void numberOff() {
		for (int i = 0; i < 5; i++) {
			num++;
			System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num);
		}
	}
}

public class SynchronizedTest {
	public static void main(String[] args) throws InterruptedException {
		for (int i = 1; i <= 3; i++) {
			new Thread(() -> { new SynchronizedExample().numberOff(); },"佇列" + i).start();
		}
	}
}

六、執行緒池實現

最後用執行緒池將上述程式碼寫一下

package ioo;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class SynchronizedExample {
	protected static int num = 0;
	protected synchronized static void numberOff() {
		for (int i = 0; i < 5; i++) {
			num++;
			System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num);
		}
	}
}

public class SynchronizedTest {
	public static void main(String[] args) throws InterruptedException {
		ExecutorService executorService = Executors.newCachedThreadPool();
		for(int i=1; i<=3; i++) {
			executorService.execute(() -> new SynchronizedExample().numberOff());
		}
	}
}

以上就是淺析Java 併發程式設計中的synchronized的詳細內容,更多關於Java 併發程式設計 synchronized的資料請關注我們其它相關文章!