基本使用:
- 加锁解锁
// 给对象加锁
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
- 可重入:加锁多少次,必须 unlock 多少次,否则对象的锁会一直不释放
// 重复加锁
lock.lock();
lock.lock();
try {
count++;
} finally {
lock.unlock();
lock.unlock();
}
- 可中断:一般发生死锁时我们可以通过其他方式主动中断线程的加锁,释放锁
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 线程已被中断退出
- 可限时:限定获取锁的最长时间,否则就不获取了
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 申请锁失败
- 公平:可以让线程获取锁的过程有一定的顺序(先申请锁,先获得锁),但是性能会差很多(要处理线程排队)
// 只要在创建 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