ReentrantLock in Java with Examples - Java 147

ReentrantLock in Java with Examples – Java 147

ReentrantLock in Java with Examples

ReentrantLock in Java 可重入的互斥鎖,又被稱為獨占鎖,實現了 Lock 介面,擁有與 synchronized 相同的同步加鎖功能,添加了類似鎖投票、定時鎖和可中斷鎖的一些特性, Reentrant Lock 同一個時間點,只能被一個執行緒持有,而可重入的意思是,可以被單個執行緒多次讀取,Java Reentrant Lock 本篇增加了範例,並透過單元測試來驗證產出結果。

檔案目錄

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

單元測試

Java Reentrant Lock 提供加鎖、解鎖等操作。

nolock

Java Reentrant Lock 建立 500 個執行緒,不加鎖對計數器加 1 ,等待 1 秒後結束任務,最終結果會不如預期。

	protected class NoLockWorker implements Runnable {

		private int count;

		public NoLockWorker() {
		}

		@Override
		public void run() {
			try {
				TimeUnit.SECONDS.sleep(1);
				count++;
			} catch (InterruptedException ex) {
				ex.printStackTrace();
			}
			System.out.println(String.format("T[%d] count: %d", Thread.currentThread().getId(), count));
		}

		public int getCount() {
			return count;
		}
	}

	@Test
	public void nolock() {
		int expected = 500;
		int taskSize = 500;
		NoLockWorker worker = new NoLockWorker();
		List<Thread> threads = Stream.generate(() -> new Thread(worker)).limit(taskSize).collect(Collectors.toList());
		threads.forEach(e -> e.start());

		threads.forEach(e -> {
			try {
				e.join();
			} catch (InterruptedException ex) {
				ex.printStackTrace();
			}
		});
		int count = worker.getCount();
		System.out.println(count);
		assertEquals(expected, count);
	}
T[358] count: 496
T[401] count: 496
T[341] count: 496
T[366] count: 496
T[376] count: 496
496

lock

Java Reentrant Lock 建立 500 個執行緒,加鎖對計數器加 1 ,然後結束任務。

	protected class Worker implements Runnable {

		private final Lock lock = new ReentrantLock();
		private int count;

		public Worker() {
		}

		@Override
		public void run() {
			lock.lock();
			try {
				System.out.println(String.format("T[%d] lock acquired", Thread.currentThread().getId()));
				//TimeUnit.SECONDS.sleep(1);
				count++;
				System.out.println(String.format("T[%d] count: %d", Thread.currentThread().getId(), count));
			} catch (Exception ex) {
				ex.printStackTrace();
			} finally {
				lock.unlock();
				System.out.println(String.format("T[%d] lock released", Thread.currentThread().getId()));
			}
		}

		public int getCount() {
			return count;
		}
	}

	@Test
	public void lock() {
		int expected = 500;
		int taskSize = 500;
		Worker worker = new Worker();
		List<Thread> threads = Stream.generate(() -> new Thread(worker)).limit(taskSize).collect(Collectors.toList());
		threads.forEach(e -> e.start());

		threads.forEach(e -> {
			try {
				e.join();
			} catch (InterruptedException ex) {
				ex.printStackTrace();
			}
		});
		int count = worker.getCount();
		System.out.println(count);
		assertEquals(expected, count);
	}
[497] count: 496
T[497] lock released
T[498] lock acquired
T[498] count: 497
T[498] lock released
T[504] lock acquired
T[504] count: 498
T[504] lock released
T[466] lock acquired
T[466] count: 499
T[466] lock released
T[507] lock acquired
T[507] count: 500
T[507] lock released
500

lockInterruptibly

Java Reentrant Lock 建立 3 個執行緒,加中斷鎖對計數器加 1 ,等待 1 秒後結束任務,中斷第 2 條執行緒,會拋出例外。

	protected class LockInterruptiblyWorker implements Runnable {

		private final Lock lock = new ReentrantLock();
		private int count;

		public LockInterruptiblyWorker() {
		}

		@Override
		public void run() {
			try {
				lock.lockInterruptibly();
				try {
					System.out.println(String.format("T[%d] lock acquired", Thread.currentThread().getId()));
					count++;
					TimeUnit.SECONDS.sleep(1);
					System.out.println(String.format("T[%d] count: %d", Thread.currentThread().getId(), count));
				} catch (Exception ex) {
					ex.printStackTrace();
				} finally {
					lock.unlock();
					System.out.println(String.format("T[%d] lock released", Thread.currentThread().getId()));
				}
			} catch (InterruptedException ex) {
				ex.printStackTrace();
			}
		}

		public int getCount() {
			return count;
		}
	}

	@Test
	public void lockInterruptibly() {
		int expected = 2;
		int taskSize = 3;
		LockInterruptiblyWorker worker = new LockInterruptiblyWorker();
		List<Thread> threads = Stream.generate(() -> new Thread(worker)).limit(taskSize).collect(Collectors.toList());
		threads.forEach(e -> e.start());

		Thread thread = threads.get(1);
		thread.interrupt();

		threads.forEach(e -> {
			try {
				e.join();
			} catch (InterruptedException ex) {
				ex.printStackTrace();
			}
		});
		int count = worker.getCount();
		System.out.println(count);
		assertEquals(expected, count);
	}
T[11] lock acquired
java.lang.InterruptedException
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
	at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
	at org.ruoxue.java_147.synchronization.ReentrantLockWithExamplesTest$LockInterruptiblyWorker.run(ReentrantLockWithExamplesTest.java:119)
	at java.lang.Thread.run(Thread.java:750)
T[11] count: 1
T[11] lock released
T[13] lock acquired
T[13] count: 2
T[13] lock released
2

tryLock

ReentrantLock in Java 建立 3 個執行緒,加等候鎖對計數器加 1 ,等待 1 秒後結束任務,有兩個執行緒等候逾時,無執行任務。

	protected class TryLockWorker implements Runnable {

		private final Lock lock = new ReentrantLock();
		private int count;

		public TryLockWorker() {
		}

		@Override
		public void run() {
			try {
				boolean isLockAcquired = lock.tryLock(100, TimeUnit.MILLISECONDS);
				System.out.println(
						String.format("T[%d] isLockAcquired: %b", Thread.currentThread().getId(), isLockAcquired));
				if (isLockAcquired) {
					try {
						System.out.println(String.format("T[%d] lock acquired", Thread.currentThread().getId()));
						count++;
						TimeUnit.SECONDS.sleep(1);
						System.out.println(String.format("T[%d] count: %d", Thread.currentThread().getId(), count));
					} catch (Exception ex) {
						ex.printStackTrace();
					} finally {
						lock.unlock();
						System.out.println(String.format("T[%d] lock released", Thread.currentThread().getId()));
					}
				}
			} catch (InterruptedException ex) {
				ex.printStackTrace();
			}
		}

		public int getCount() {
			return count;
		}
	}

	@Test
	public void tryLock() {
		int expected = 1;
		int taskSize = 3;
		TryLockWorker worker = new TryLockWorker();
		List<Thread> threads = Stream.generate(() -> new Thread(worker)).limit(taskSize).collect(Collectors.toList());
		threads.forEach(e -> e.start());

		threads.forEach(e -> {
			try {
				e.join();
			} catch (InterruptedException ex) {
				ex.printStackTrace();
			}
		});
		int count = worker.getCount();
		System.out.println(count);
		assertEquals(expected, count);
	}
T[12] isLockAcquired: true
T[12] lock acquired
T[11] isLockAcquired: false
T[13] isLockAcquired: false
T[12] count: 1
T[12] lock released
1

ReentrantLockWithExamplesTest.java

ReentrantLock in Java 新增單元測試,驗證 Reentrant Lock 是否符合預期。

package org.ruoxue.java_147.synchronization.reentrantlock;

import static org.junit.Assert.*;

import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.Test;

public class ReentrantLockWithExamplesTest {

	protected class NoLockWorker implements Runnable {

		private int count;

		public NoLockWorker() {
		}

		@Override
		public void run() {
			try {
				TimeUnit.SECONDS.sleep(1);
				count++;
			} catch (InterruptedException ex) {
				ex.printStackTrace();
			}
			System.out.println(String.format("T[%d] count: %d", Thread.currentThread().getId(), count));
		}

		public int getCount() {
			return count;
		}
	}

	@Test
	public void nolock() {
		int expected = 500;
		int taskSize = 500;
		NoLockWorker worker = new NoLockWorker();
		List<Thread> threads = Stream.generate(() -> new Thread(worker)).limit(taskSize).collect(Collectors.toList());
		threads.forEach(e -> e.start());

		threads.forEach(e -> {
			try {
				e.join();
			} catch (InterruptedException ex) {
				ex.printStackTrace();
			}
		});
		int count = worker.getCount();
		System.out.println(count);
		assertEquals(expected, count);
	}

	protected class Worker implements Runnable {

		private final Lock lock = new ReentrantLock();
		private int count;

		public Worker() {
		}

		@Override
		public void run() {
			lock.lock();
			try {
				System.out.println(String.format("T[%d] lock acquired", Thread.currentThread().getId()));
				//TimeUnit.SECONDS.sleep(1);
				count++;
				System.out.println(String.format("T[%d] count: %d", Thread.currentThread().getId(), count));
			} catch (Exception ex) {
				ex.printStackTrace();
			} finally {
				lock.unlock();
				System.out.println(String.format("T[%d] lock released", Thread.currentThread().getId()));
			}
		}

		public int getCount() {
			return count;
		}
	}

	@Test
	public void lock() {
		int expected = 500;
		int taskSize = 500;
		Worker worker = new Worker();
		List<Thread> threads = Stream.generate(() -> new Thread(worker)).limit(taskSize).collect(Collectors.toList());
		threads.forEach(e -> e.start());

		threads.forEach(e -> {
			try {
				e.join();
			} catch (InterruptedException ex) {
				ex.printStackTrace();
			}
		});
		int count = worker.getCount();
		System.out.println(count);
		assertEquals(expected, count);
	}

	protected class LockInterruptiblyWorker implements Runnable {

		private final Lock lock = new ReentrantLock();
		private int count;

		public LockInterruptiblyWorker() {
		}

		@Override
		public void run() {
			try {
				lock.lockInterruptibly();
				try {
					System.out.println(String.format("T[%d] lock acquired", Thread.currentThread().getId()));
					TimeUnit.SECONDS.sleep(1);
					count++;
					System.out.println(String.format("T[%d] count: %d", Thread.currentThread().getId(), count));
				} catch (Exception ex) {
					ex.printStackTrace();
				} finally {
					lock.unlock();
					System.out.println(String.format("T[%d] lock released", Thread.currentThread().getId()));
				}
			} catch (InterruptedException ex) {
				ex.printStackTrace();
			}
		}

		public int getCount() {
			return count;
		}
	}

	@Test
	public void lockInterruptibly() {
		int expected = 2;
		int taskSize = 3;
		LockInterruptiblyWorker worker = new LockInterruptiblyWorker();
		List<Thread> threads = Stream.generate(() -> new Thread(worker)).limit(taskSize).collect(Collectors.toList());
		threads.forEach(e -> e.start());

		Thread thread = threads.get(1);
		thread.interrupt();

		threads.forEach(e -> {
			try {
				e.join();
			} catch (InterruptedException ex) {
				ex.printStackTrace();
			}
		});
		int count = worker.getCount();
		System.out.println(count);
		assertEquals(expected, count);
	}

	protected class TryLockWorker implements Runnable {

		private final Lock lock = new ReentrantLock();
		private int count;

		public TryLockWorker() {
		}

		@Override
		public void run() {
			try {
				boolean isLockAcquired = lock.tryLock(100, TimeUnit.MILLISECONDS);
				System.out.println(
						String.format("T[%d] isLockAcquired: %b", Thread.currentThread().getId(), isLockAcquired));
				if (isLockAcquired) {
					try {
						System.out.println(String.format("T[%d] lock acquired", Thread.currentThread().getId()));
						TimeUnit.SECONDS.sleep(1);
						count++;
						System.out.println(String.format("T[%d] count: %d", Thread.currentThread().getId(), count));
					} catch (Exception ex) {
						ex.printStackTrace();
					} finally {
						lock.unlock();
						System.out.println(String.format("T[%d] lock released", Thread.currentThread().getId()));
					}
				}
			} catch (InterruptedException ex) {
				ex.printStackTrace();
			}
		}

		public int getCount() {
			return count;
		}
	}

	@Test
	public void tryLock() {
		int expected = 1;
		int taskSize = 3;
		TryLockWorker worker = new TryLockWorker();
		List<Thread> threads = Stream.generate(() -> new Thread(worker)).limit(taskSize).collect(Collectors.toList());
		threads.forEach(e -> e.start());

		threads.forEach(e -> {
			try {
				e.join();
			} catch (InterruptedException ex) {
				ex.printStackTrace();
			}
		});
		int count = worker.getCount();
		System.out.println(count);
		assertEquals(expected, count);
	}
}

心得分享

Java ReentrantLock Examples 訪問共享資源時提供方法同步,操作共享資源的程式碼被鎖定和解鎖方法的調用所包圍,這會鎖定當前工作執行緒,並阻止所有其他試圖鎖定共享資源的執行緒, ReentrantLock in Java 提供了更多,更加全面的功能,具備更強的擴展性,相比 synchronized 而言, Reentrant Lock 比較不容易產生死鎖。

發佈留言