目录
线程活跃状态
死锁
当一个线程需要同时获取多把锁时,容易发生死锁。
@Slf4j(topic = "c.DeadLock") public class DeadLock { public static void main(String[] args) { Object lock1 = new Object(); Object lock2 = new Object(); new Thread(() -> { synchronized (lock1){ log.debug("获取 lock1"); try { Thread.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock2){ log.debug("获取 lock2"); } } }, "t1").start(); new Thread(() -> { synchronized (lock2){ log.debug("获取 lock2"); try { Thread.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock1){ log.debug("获取 lock1"); } } }, "t2").start(); } }
① 线程 t1 在对象 lock2 指向的 monitor 中的 EntryList 里,BLOCKED;等待线程 t2 释放
② 线程 t2 在对象 lock1 指向的 monitor 中的 EntryList 里,BLOCKED;等待线程 t1 释放
③ t1 等 t2 放,t2 等 t1 放
就像小情侣吵完架,都等着对方先道歉;于是死锁就产生了
活锁
两个线程相互改变对方结束条件,于是两个线程永远都结束不了
@Slf4j(topic = "c.LiveLock") public class LiveLock { private static int count = 10; public static void main(String[] args) { new Thread(() -> { while(count < 20){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("{}", ++count); } }, "t1").start(); new Thread(() -> { while(count > 0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("{}", --count); } }, "t2").start(); } }
count 永远达不到 t1 和 t2 所需要的结束条件,于是两个线程永远都不会结束
解决:提高两个线程睡眠时间长度差距,使得一个线程比另一个线程更早执行完
饥饿
一个线程由于优先级太低,始终得不到 CPU 调度执行,也不能够结束
@Slf4j(topic = "c.HungryLock") public class HungryLock { public static void main(String[] args) { Object lock1 = new Object(); Object lock2 = new Object(); new Thread(() -> { int count = 0; while (true){ synchronized (lock1){ synchronized (lock2){ log.debug("Java...... {}", ++count); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } }, "Java").start(); new Thread(() -> { int count = 0; while(true){ synchronized (lock1){ synchronized (lock2){ log.debug("C++...... {}", ++count); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } }, "C++").start(); new Thread(() -> { int count = 0; while(true){ synchronized (lock1){ synchronized (lock2){ log.debug("Python...... {}", ++count); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } }, "Python").start(); new Thread(() -> { int count = 0; while(true){ synchronized (lock1){ synchronized (lock2){ log.debug("Go...... {}", ++count); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } }, "Go").start(); } }
① 顺序加锁可以解决死锁问题;因为当线程获取不到第一把锁时,它就不会进入同步代码块
② 但是产生了饥饿问题;某些线程要等很久才能运行,甚至运行不了
ReentrantLock
特点
① 可重入
② 可打断
③ 可设置超时时间
④ 可设置为公平锁
⑤ 支持多个条件变量
lock.lock(); // 获取锁 try { // 临界区 }finally { lock.unlock(); // 释放锁 }
特点展示
可重入
@Slf4j(topic = "c.ReentrantLockTest") class Reentrant{ private ReentrantLock lock = new ReentrantLock(); void method1(){ log.debug("第一层方法"); lock.lock(); try { method2(); }finally { lock.unlock(); } } void method2(){ log.debug("第二层方法"); lock.lock(); try { method3(); }finally { lock.unlock(); } } void method3(){ lock.lock(); try { log.debug("第三层方法"); }finally { lock.unlock(); } } }
可打断
static void interrupt() throws InterruptedException { Thread t1 = new Thread(() -> { try { lock.lockInterruptibly(); // 必须调用此方法 } catch (InterruptedException e) { log.debug("等待锁过程被打断..."); e.printStackTrace(); return; } try { log.debug("获取锁成功"); }finally { lock.unlock(); log.debug("释放锁成功"); } }, "t1"); t1.start(); lock.lock(); log.debug("获得锁"); Thread.sleep(1000); log.debug("进行打断"); t1.interrupt(); }
① lock.lockInterruptibly() 才能被打断
② 支持可打断,避免线程一直等待锁
可设置超时时间
t1 立即尝试获得锁;t2 设置获取锁超时时间
static void tryLock() throws InterruptedException { Thread t1 = new Thread(() -> { if(!lock.tryLock()){ log.debug("立即尝试获取锁失败..."); return; } try{ log.debug("立即尝试获取锁成功!"); }finally { lock.unlock(); } }, "t1"); Thread t2 = new Thread(() -> { try { if(!lock.tryLock(2, TimeUnit.MINUTES)){ log.debug("尝试获取锁,超过 2s,获取锁失败..."); } } catch (InterruptedException e) { e.printStackTrace(); } try { log.debug("尝试获取锁,小于 2s,获取锁成功!"); }finally { lock.unlock(); } }, "t2"); try{ lock.lock(); log.debug("获取锁成功"); t1.start(); t2.start(); Thread.sleep(1000); log.debug("睡 1s,放锁"); }finally { lock.unlock(); } }
① t1 立即尝试获取锁,失败
② t2 超时时间为 2s,main 线程 1s 后释放锁,t2 获取锁成功
可设置为公平锁
① ReentrantLock 默认为步公平
② 公平锁:按照先来后到的规则
③ 公平锁一般没必要,会降低并发度
1、不公平情况
static void unfairLock() throws InterruptedException { ReentrantLock unfairLock = new ReentrantLock(); // 不公平情况 for(int i = 0; i < 500; i++){ int index = i; new Thread(() -> { unfairLock.lock(); try{ log.debug("t{}, 获得锁", index); } finally { unfairLock.unlock(); } }, "t" + 1).start(); } Thread.sleep(10); new Thread(() -> { try{ unfairLock.lock(); log.debug("不公平插入"); }finally { unfairLock.unlock(); } }, "不公平").start(); }
① 按公平来说,此不公平线程应该要最后才能拿到锁
② 其他线程正好释放锁,它正好启动获取锁,于是出现这种不公平插入情况
2、公平情况
ReentrantLock unfairLock = new ReentrantLock(true);
等到循环中线程全部获取锁并释放,它才能获得锁
支持多个条件变量
相当于 ReentrantLock 有多个 WaitSet,不同的条件对应不同的 WaitSet
static void conditionLock() throws InterruptedException { ReentrantLock conditionalLock = new ReentrantLock(); Condition javaWait = conditionalLock.newCondition(); // 进入 Java 休息室 Condition cWait = conditionalLock.newCondition(); // 进入 C 休息室 Condition pythonWait = conditionalLock.newCondition(); // 进入 Python 休息室 Condition goWait = conditionalLock.newCondition(); // 进入 Go 休息室 Condition otherWait = conditionalLock.newCondition(); // 进入其他休息室 Random random = new Random(); // 线程进入随机休息室 for(int i = 0; i < 10; i++){ new Thread(() -> { conditionalLock.lock(); try{ int index = random.nextInt(6); switch (index){ case 1 : { log.debug("进入 Java 休息室,一直等待"); javaWait.await(); log.debug("从 Java 休息室出来"); break; } case 2 : { log.debug("进入 C 休息室,一直等待"); cWait.await(); log.debug("从 C 休息室出来"); break; } case 3 : { log.debug("进入 Python 休息室,一直等待"); pythonWait.await(); log.debug("从 Python 休息室出来"); break; } case 4 : { log.debug("进入 Go 休息室,一直等待"); goWait.await(); log.debug("从 Go 休息室出来"); break; } default : { log.debug("进入其他休息室,只等待 2s"); otherWait.await(2, TimeUnit.SECONDS); log.debug("超过 2s 了,在其他休息室自动出来"); } } } catch (InterruptedException e) { e.printStackTrace(); } finally { conditionalLock.unlock(); } }, "t" + i).start(); } Thread.sleep(2000); conditionalLock.lock(); try { log.debug("-----------------随机唤醒 Java休息室 中某个线程-----------------"); javaWait.signal(); Thread.sleep(1000); log.debug("-----------------随机唤醒 C 休息室 中某个线程-----------------"); cWait.signal(); Thread.sleep(1000); log.debug("-----------------随机唤醒 Python 休息室 中某个线程-----------------"); pythonWait.signal(); Thread.sleep(1000); log.debug("-----------------随机唤醒 Go 休息室 中所以线程-----------------"); goWait.signalAll(); }finally { conditionalLock.unlock(); } }
解决死锁问题
都不要那么自私,尝试获取锁失败,就把手中已有锁释放,让别人先用
static void solveDeadLock(){ ReentrantLock lock1 = new ReentrantLock(); ReentrantLock lock2 = new ReentrantLock(); ReentrantLock lock3 = new ReentrantLock(); ReentrantLock lock4 = new ReentrantLock(); new Thread(() -> { while (true){ if(lock1.tryLock()){ try{ if(lock2.tryLock()){ try { log.debug("Java......"); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock2.unlock(); } } }finally { lock1.unlock(); } } } }, "Java").start(); new Thread(() -> { while(true){ if(lock2.tryLock()){ try{ if(lock3.tryLock()){ try { log.debug("C++......"); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock3.unlock(); } } }finally { lock2.unlock(); } } } }, "C++").start(); new Thread(() -> { while(true){ if(lock3.tryLock()){ try{ if(lock4.tryLock()){ try { log.debug("Python......"); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock4.unlock(); } } }finally { lock3.unlock(); } } } }, "Python").start(); new Thread(() -> { while(true){ if(lock4.tryLock()){ try{ if(lock1.tryLock()){ try { log.debug("Go......"); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock1.unlock(); } } }finally { lock4.unlock(); } } } }, "Go").start(); }