Java Synchronized Keyword - Java 147

Java Synchronized Keyword – Java 147

Java Synchronized Keyword

使用 Synchronized 關鍵字,控制多個執行緒訪問任何共享資源的能力,來確保在給定的時間點,只有一個執行緒可以訪問資源, Synchronized Keyword in Java 本篇增加了範例,並透過單元測試來驗證產出結果。

檔案目錄

./
   +- src
       +- test
       |   +- org
       |       +- ruoxue
       |           +- java_147
       |               +- synchronization
       |                   +- SynchronizedKeywordTest.java   

單元測試

Java Synchronized 提供同步等操作。

syncStatic

建立 3 個執行緒,在 static 方法上加 synchronized ,對計數器加 1 ,然後結束任務。

	protected static class SyncStaticCounter {

		private static int count;

		public static synchronized void increment() {
			count = getCount() + 1;
			System.out.println(String.format("T[%d] count: %d", Thread.currentThread().getId(), count));
		}

		public static int getCount() {
			return count;
		}
	}

	@Test
	public void syncStatic() {
		int expected = 1000;
		int taskSize = 1000;
		ExecutorService executorService = Executors.newFixedThreadPool(3);
		IntStream.range(0, taskSize).forEach(e -> {
			executorService.submit(SyncStaticCounter::increment);
		});

		executorService.shutdown();
		try {
			if (!executorService.awaitTermination(3, TimeUnit.SECONDS)) {
				executorService.shutdownNow();
			}
		} catch (InterruptedException e) {
			executorService.shutdownNow();
		}
		int count = SyncStaticCounter.getCount();
		System.out.println(count);
		assertEquals(expected, count);
	}
T[13] count: 996
T[13] count: 997
T[13] count: 998
T[11] count: 999
T[12] count: 1000
1000

reentrant

Java Synchronized 建立 3 個執行緒,調用 1 個有 synchronized 的方法,此方法內又調用另一個有 synchronized 的方法,然後結束任務。

	protected class ReentrantCounter {

		private int count;

		public ReentrantCounter() {
		}

		public synchronized void increment() {
			synchronized (this) {
				doIncrement();
			}
		}

		public synchronized void doIncrement() {
			synchronized (this) {
				count = getCount() + 1;
				System.out.println(String.format("T[%d] count: %d", Thread.currentThread().getId(), count));
			}
		}

		public int getCount() {
			return count;
		}
	}

	@Test
	public void reentrant() {
		int expected = 1000;
		int taskSize = 1000;
		ExecutorService executorService = Executors.newFixedThreadPool(3);
		ReentrantCounter counter = new ReentrantCounter();
		IntStream.range(0, taskSize).forEach(e -> {
			executorService.submit(counter::increment);
		});

		executorService.shutdown();
		try {
			if (!executorService.awaitTermination(3, TimeUnit.SECONDS)) {
				executorService.shutdownNow();
			}
		} catch (InterruptedException e) {
			executorService.shutdownNow();
		}
		int count = counter.getCount();
		System.out.println(count);
		assertEquals(expected, count);
	}
T[12] count: 996
T[12] count: 997
T[12] count: 998
T[11] count: 999
T[13] count: 1000
1000

deadlock

Java Synchronized 建立 2 個執行緒,每個執行緒各自取得 2 個鎖,產生死鎖,無法中斷執行。

	@Test
	public void deadlock() {
		Object lock1 = new Object();
		Object lock2 = new Object();
		Thread threadA = new Thread(() -> {
			String id = "A";
			synchronized (lock1) {
				System.out
						.println(String.format("T[%d] counter: %s lock1 acquired", Thread.currentThread().getId(), id));
				try {
					Thread.sleep(3);
				} catch (InterruptedException ex) {
					ex.printStackTrace();
				}
				synchronized (lock2) {
					System.out.println(
							String.format("T[%d] counter: %s lock2 acquired", Thread.currentThread().getId(), id));

				}
			}
		});

		Thread threadB = new Thread(() -> {
			String id = "B";
			synchronized (lock2) {
				System.out
						.println(String.format("T[%d] counter: %s lock2 acquired", Thread.currentThread().getId(), id));
				try {
					Thread.sleep(3);
				} catch (InterruptedException ex) {
					ex.printStackTrace();
				}
				synchronized (lock1) {
					System.out.println(
							String.format("T[%d] counter: %s lock1 acquired", Thread.currentThread().getId(), id));

				}
			}
		});

		threadB.start();
		threadA.start();

		try {
			threadA.join();
			threadB.join();
		} catch (InterruptedException ex) {
			ex.printStackTrace();
		}
	}
T[12] counter: B lock2 acquired
T[11] counter: A lock1 acquired

deadlockSolution

Java Synchronized 建立 2 個執行緒,每個執行緒各自取得 2 個鎖,產生死鎖,修正取鎖的順序,解決死鎖的問題。

	@Test
	public void deadlockSolution() {
		Object lock1 = new Object();
		Object lock2 = new Object();
		Thread threadA = new Thread(() -> {
			String id = "A";
			synchronized (lock1) {
				System.out
						.println(String.format("T[%d] counter: %s lock1 acquired", Thread.currentThread().getId(), id));
				try {
					Thread.sleep(3);
				} catch (InterruptedException ex) {
					ex.printStackTrace();
				}
				synchronized (lock2) {
					System.out.println(
							String.format("T[%d] counter: %s lock2 acquired", Thread.currentThread().getId(), id));

				}
			}
		});

		Thread threadB = new Thread(() -> {
			String id = "B";
			synchronized (lock1) {
				System.out
						.println(String.format("T[%d] counter: %s lock2 acquired", Thread.currentThread().getId(), id));
				try {
					Thread.sleep(3);
				} catch (InterruptedException ex) {
					ex.printStackTrace();
				}
				synchronized (lock2) {
					System.out.println(
							String.format("T[%d] counter: %s lock1 acquired", Thread.currentThread().getId(), id));

				}
			}
		});

		threadB.start();
		threadA.start();

		try {
			threadA.join();
			threadB.join();
		} catch (InterruptedException ex) {
			ex.printStackTrace();
		}
	}
T[12] counter: B lock2 acquired
T[12] counter: B lock1 acquired
T[11] counter: A lock1 acquired
T[11] counter: A lock2 acquired

SynchronizedKeywordTest.java

Synchronized Java 新增單元測試,驗證是否符合預期。

package org.ruoxue.java_147.synchronization;

import static org.junit.Assert.*;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

import org.junit.Test;

public class SynchronizedKeywordTest {

	protected static class SyncStaticCounter {

		private static int count;

		public static synchronized void increment() {
			count = getCount() + 1;
			System.out.println(String.format("T[%d] count: %d", Thread.currentThread().getId(), count));
		}

		public static int getCount() {
			return count;
		}
	}

	@Test
	public void syncStatic() {
		int expected = 1000;
		int taskSize = 1000;
		ExecutorService executorService = Executors.newFixedThreadPool(3);
		IntStream.range(0, taskSize).forEach(e -> {
			executorService.submit(SyncStaticCounter::increment);
		});

		executorService.shutdown();
		try {
			if (!executorService.awaitTermination(3, TimeUnit.SECONDS)) {
				executorService.shutdownNow();
			}
		} catch (InterruptedException e) {
			executorService.shutdownNow();
		}
		int count = SyncStaticCounter.getCount();
		System.out.println(count);
		assertEquals(expected, count);
	}

	protected class ReentrantCounter {

		private int count;

		public ReentrantCounter() {
		}

		public synchronized void increment() {
			synchronized (this) {
				doIncrement();
			}
		}

		public synchronized void doIncrement() {
			synchronized (this) {
				count = getCount() + 1;
				System.out.println(String.format("T[%d] count: %d", Thread.currentThread().getId(), count));
			}
		}

		public int getCount() {
			return count;
		}
	}

	@Test
	public void reentrant() {
		int expected = 1000;
		int taskSize = 1000;
		ExecutorService executorService = Executors.newFixedThreadPool(3);
		ReentrantCounter counter = new ReentrantCounter();
		IntStream.range(0, taskSize).forEach(e -> {
			executorService.submit(counter::increment);
		});

		executorService.shutdown();
		try {
			if (!executorService.awaitTermination(3, TimeUnit.SECONDS)) {
				executorService.shutdownNow();
			}
		} catch (InterruptedException e) {
			executorService.shutdownNow();
		}
		int count = counter.getCount();
		System.out.println(count);
		assertEquals(expected, count);
	}

	@Test
	public void deadlock() {
		Object lock1 = new Object();
		Object lock2 = new Object();
		Thread threadA = new Thread(() -> {
			String id = "A";
			synchronized (lock1) {
				System.out
						.println(String.format("T[%d] counter: %s lock1 acquired", Thread.currentThread().getId(), id));
				try {
					Thread.sleep(3);
				} catch (InterruptedException ex) {
					ex.printStackTrace();
				}
				synchronized (lock2) {
					System.out.println(
							String.format("T[%d] counter: %s lock2 acquired", Thread.currentThread().getId(), id));

				}
			}
		});

		Thread threadB = new Thread(() -> {
			String id = "B";
			synchronized (lock2) {
				System.out
						.println(String.format("T[%d] counter: %s lock2 acquired", Thread.currentThread().getId(), id));
				try {
					Thread.sleep(3);
				} catch (InterruptedException ex) {
					ex.printStackTrace();
				}
				synchronized (lock1) {
					System.out.println(
							String.format("T[%d] counter: %s lock1 acquired", Thread.currentThread().getId(), id));

				}
			}
		});

		threadB.start();
		threadA.start();

		try {
			threadA.join();
			threadB.join();
		} catch (InterruptedException ex) {
			ex.printStackTrace();
		}
	}

	@Test
	public void deadlockSolution() {
		Object lock1 = new Object();
		Object lock2 = new Object();
		Thread threadA = new Thread(() -> {
			String id = "A";
			synchronized (lock1) {
				System.out
						.println(String.format("T[%d] counter: %s lock1 acquired", Thread.currentThread().getId(), id));
				try {
					Thread.sleep(3);
				} catch (InterruptedException ex) {
					ex.printStackTrace();
				}
				synchronized (lock2) {
					System.out.println(
							String.format("T[%d] counter: %s lock2 acquired", Thread.currentThread().getId(), id));

				}
			}
		});

		Thread threadB = new Thread(() -> {
			String id = "B";
			synchronized (lock1) {
				System.out
						.println(String.format("T[%d] counter: %s lock2 acquired", Thread.currentThread().getId(), id));
				try {
					Thread.sleep(3);
				} catch (InterruptedException ex) {
					ex.printStackTrace();
				}
				synchronized (lock2) {
					System.out.println(
							String.format("T[%d] counter: %s lock1 acquired", Thread.currentThread().getId(), id));

				}
			}
		});

		threadB.start();
		threadA.start();

		try {
			threadA.join();
			threadB.join();
		} catch (InterruptedException ex) {
			ex.printStackTrace();
		}
	}
}

心得分享

Java Synchronized Method 提供了一種建立執行緒並使用同步區塊同步任務的方法,使用 synchronized 關鍵字,同步區塊在某個物件上同步,所有在同一物件上同步的同步區塊,一次只能有一個執行緒在其中執行,所有其他試圖進入同步區塊的執行緒都被阻塞,直到同步區塊內的執行緒退出同步區塊, Synchronized Java 提供了幾種 Synchronized 常見方法的操作範例。

發佈留言