JUC--活跃性

JUC–活跃性

  • JUC–活跃性

  • 博主以黑马JUC进行学习

    死锁

    有这样的情况:一个线程需要同时获取多把锁,这时就容易发生死锁

    t1 线程获得 A对象 锁,接下来想获取B 对象的锁

    t2 线程获得 B对象 锁,接下来想获取 A对象的锁

    例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    Object A = new Object();
    Object B = new Object();
    Thread t1 = new Thread(() -> {
    synchronized(A){
    log.debug("lock A");
    sleep(1);
    synchronized(B){
    log.debug("lock B");
    log.debug("操作...");
    }
    }
    },"t1");

    Thread t2 = new Thread(() -> {
    synchronized(B){
    log.debug("lock B");
    sleep(0.5);
    synchronized(A){
    log.debug("lock A");
    log.debug("操作...");
    }
    }
    },"t2")

    定位死锁

    • 检测死锁可以使用jconsole工具,或者使用jps定位进程id,再用jstack定位死锁:
    1
    2
    3
    4
    5
    6
    7
    cmd > jps
    Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8
    12320 Jps
    22816 KotlinCompileDaemon
    33200 TestDeadLock //JVM 进程
    11508 Main
    28468 Launcher

    活锁

    活锁出现在两个线程互相改变对方的结束条件,最后谁也无法结束,例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public class TestLiveLock{
    static volatile int count = 10;
    static final Object lock = new Object();

    public static void main(String[] args){
    new Thread(() -> {
    //期望减到0退出循环
    while(count > 0){
    sleep(0.2);
    count--;
    log.debug("count:{}",count);
    }
    },"t1").start();

    new Thread(() -> {
    //期望超过20退出循环
    while(count < 20){
    sleep(0.2);
    count++;
    log.debug("count:{}",count);
    }
    },"t2").start();
    }
    }

    饥饿

    • 某个线程长期得不到 CPU 执行权,或长期抢不到锁,导致任务一直无法执行。
    • 常见原因:
      • 线程优先级太低
      • 其他线程长期占用锁
      • 非公平锁导致部分线程永远抢不到
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // 非公平锁 + 高频率抢锁,低优先级线程可能饥饿
    Lock lock = new ReentrantLock(false);

    new Thread(()->{
    while(true){
    lock.lock();
    try {
    // 长时间占用锁
    Thread.sleep(1000);
    } finally {
    lock.unlock();
    }
    }
    },"高频率抢锁线程").start();

    // 这个线程可能永远抢不到锁 → 饥饿
    new Thread(()->{
    lock.lock();
    try {
    System.out.println("我执行到了吗?");
    } finally {
    lock.unlock();
    }
    },"饥饿线程").start();

    对比

    问题 线程状态 资源占用 结果
    死锁 阻塞(BLOCKED) CPU 0% 永久卡住
    活锁 运行(RUNNABLE) CPU 100% 永远完不成
    饥饿 等待 / 阻塞 CPU 极低 一直得不到执行机会

    总结

    死锁:互相持有锁,永久阻塞

    活锁:互相改条件,一直运行但永远完不成

    饥饿:一直抢不到资源,无法执行

    都是活跃性失败,会让并发程序无法正常工作