Java 多线程高并发 3.1 — ReentrantLock 重入锁简单使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ocp114/article/details/82828269

基本使用:

  1. 加锁解锁
// 给对象加锁
lock.lock();
try {
	count++;
} finally {
	lock.unlock();
}
  1. 可重入:加锁多少次,必须 unlock 多少次,否则对象的锁会一直不释放
// 重复加锁
lock.lock();
lock.lock();
try {
	count++;
} finally {
	lock.unlock();
	lock.unlock();
}
  1. 可中断:一般发生死锁时我们可以通过其他方式主动中断线程的加锁,释放锁
public class TestReentrantLockIneterrupt {

	// 定义两个 ReentrantLock 用于互相加锁,产生死锁
	private static ReentrantLock lock1 = new ReentrantLock();
	private static ReentrantLock lock2 = new ReentrantLock();
	
	// 互相加锁,用于产生死锁
	static class TestReentrant1 implements Runnable {
		@Override
		public void run() {
			try {
				try {
					lock1.lockInterruptibly();
				} catch (InterruptedException e) {} 
				Thread.sleep(500);
				lock2.lockInterruptibly();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} finally {
				if (lock1.isHeldByCurrentThread()) lock1.unlock();
				if (lock2.isHeldByCurrentThread()) lock2.unlock();
			}
		}
	}
	
	static class TestReentrant2 implements Runnable {
		@Override
		public void run() {
			try {
				try {
					lock2.lockInterruptibly();
				} catch (InterruptedException e) {} 
				Thread.sleep(500);
				lock1.lockInterruptibly();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} finally {
				if (lock1.isHeldByCurrentThread()) lock1.unlock();
				if (lock2.isHeldByCurrentThread()) lock2.unlock();
			}
		}
	}
	
	// 死锁检查线程
	static class DeadLockCheaker implements Runnable {
		private static final ThreadMXBean mxBean = ManagementFactory.getThreadMXBean();
		@Override
		public void run() {
			while (true) {
				// 找出所有死锁的线程
				long[] deadlockedThreads = mxBean.findDeadlockedThreads();
				if (deadlockedThreads != null) {
					// 解析死锁的线程信息
					ThreadInfo[] infos = mxBean.getThreadInfo(deadlockedThreads);
					// 遍历当前程序的所有线程
					for (Thread t : Thread.getAllStackTraces().keySet()) {
						for (ThreadInfo info : infos) {
							if (info.getThreadId() == t.getId()) {
								// 发出中断指令,让死锁线程中断
								t.interrupt();
								System.out.println(t.getName() + " 线程已被中断退出 ");
							}
						}
					}
				}
			}
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		Thread t1 = new Thread(new TestReentrant1(), "Thread TestReentrant1");
		Thread t2 = new Thread(new TestReentrant2(), "Thread TestReentrant2");
		t1.start();
		t2.start();
		
		Thread.sleep(5000);
		Thread t3 = new Thread(new DeadLockCheaker());
		t3.setDaemon(true); // 设置为守护线程,否侧会死循环
		t3.start();
	}
}

输出结果
Thread TestReentrant2 线程已被中断退出
Thread TestReentrant1 线程已被中断退出

  1. 可限时:限定获取锁的最长时间,否则就不获取了
public class TestReentrantLockTimeLimit {
	
	private static ReentrantLock lock = new ReentrantLock();
	
	static class TestReentrant implements Runnable {
		@Override
		public void run() {
			try {
				if (lock.tryLock(5, TimeUnit.SECONDS)) {
					System.out.println(" 线程: " + Thread.currentThread().getName() + " 申请锁成功,沉睡 6 秒");
					Thread.sleep(6000);
				}else {
					System.out.println(" 线程: " + Thread.currentThread().getName() + " 申请锁失败");
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			} finally {
				if (lock.isHeldByCurrentThread()) lock.unlock();
			}
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		Thread t1 = new Thread(new TestReentrant(), "Thread TestReentrant1");
		Thread t2 = new Thread(new TestReentrant(), "Thread TestReentrant2");
		t1.start();
		t2.start();
	}

}

输出结果
线程: Thread TestReentrant1 申请锁成功,沉睡 6 秒
线程: Thread TestReentrant2 申请锁失败

  1. 公平:可以让线程获取锁的过程有一定的顺序(先申请锁,先获得锁),但是性能会差很多(要处理线程排队)
// 只要在创建 ReentrantLock 实例的时候加入一个参数就行
ReentrantLock lock = new ReentrantLock(true);

ReentrantLock 与 synchronized 性能对比:,jdk 8,其他参数全部默认

public class TestReentrantLock {
	
	private static ReentrantLock lock = new ReentrantLock();
	private static int count = 0;
	
	static class TestReentrant implements Runnable {
		@Override
		public void run() {
			for (int i = 0; i < 1000000; i++) {
				// 给对象加锁
				lock.lock();
				try {
					count++;
				} finally {
					// 加锁之后,执行完需要的代码,必须要 unlock,前面 lock 了多少次,后面就需要 unlock 多少次,否则对象的锁会一直不释放
					lock.unlock();
				}
			}
		}
	}
	
	static class TestSyn implements Runnable {
		@Override
		public void run() {
			for (int i = 0; i < 1000000; i++) {
				synchronized (this) {
					count++;
				}
			}
		}
	}
	
	/**
	 * 各开启 10 个 线程分别测试 ReentrantLock 和 synchronized 的性能
	 * @throws InterruptedException
	 */
	private static void compare() throws InterruptedException {
		Thread[] threads = new Thread[10];
		long start = System.currentTimeMillis();
		TestReentrant reentrant = new TestReentrant();
		for (int i = 0; i < threads.length; i++) threads[i] = new Thread(reentrant, "Thread " + i);
		for (int i = 0; i < threads.length; i++) threads[i].start();
		for (int i = 0; i < threads.length; i++) threads[i].join();
		long end1 = System.currentTimeMillis();
		
		count = 0;
		TestSyn syn = new TestSyn();
		for (int i = 0; i < threads.length; i++) threads[i] = new Thread(syn, "Thread " + i);
		for (int i = 0; i < threads.length; i++) threads[i].start();
		for (int i = 0; i < threads.length; i++) threads[i].join();
		long end2 = System.currentTimeMillis();
		
		System.out.println("ReentrantLock 耗时: " + (end1 -start) + ", synchronized 耗时: " + (end2 - end1) + ", count = " + count);
	}
	
	public static void main(String[] args) throws InterruptedException {
		for (int i = 0; i < 10; i++) {
			compare();
		}
	}
}

下面是输出:目测 ReentrantLock 性能比 synchronized 差 50% 左右
ReentrantLock 耗时: 350, synchronized 耗时: 180, count = 10000000
ReentrantLock 耗时: 335, synchronized 耗时: 130, count = 10000000
ReentrantLock 耗时: 328, synchronized 耗时: 113, count = 10000000
ReentrantLock 耗时: 328, synchronized 耗时: 111, count = 10000000
ReentrantLock 耗时: 321, synchronized 耗时: 114, count = 10000000
ReentrantLock 耗时: 334, synchronized 耗时: 113, count = 10000000
ReentrantLock 耗时: 336, synchronized 耗时: 134, count = 10000000
ReentrantLock 耗时: 335, synchronized 耗时: 129, count = 10000000
ReentrantLock 耗时: 331, synchronized 耗时: 128, count = 10000000
ReentrantLock 耗时: 327, synchronized 耗时: 111, count = 10000000

猜你喜欢

转载自blog.csdn.net/ocp114/article/details/82828269