Table of Contents
ToggleJava 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 常見方法的操作範例。