活跃性问题:死锁、活锁、饥饿

本文探讨了死锁的概念,包括其在并发编程中的形成机制、常见的例子和数据库处理方式。重点介绍了死锁的四个必要条件,以及如何通过命令行和工具定位死锁,并给出了修复策略,如避免死锁、设置超时和使用并发工具。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是死锁

1.发生在并发中

2.互不相让:当两个(或更多)线程相互持有对方需要的资源,又不主动释放,导致所有线程都无法继续进行,使得两个(或多个)线程陷入无限的阻塞,这就是死锁

两个线程造成的死锁

public class DeadLock {
    public static void main(String[] args) throws InterruptedException {
        new Thread(DeadLock::command).start();
        new Thread(DeadLock::command1).start();
    }
    static void command() {
        synchronized (Object.class) {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (DeadLock.class) {

            }
        }
        System.out.println("command_over");
    }
    static void command1() {
        synchronized (DeadLock.class) {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (Object.class) {

            }
        }
        System.out.println("command1_over");
    }
}

在这里插入图片描述

在这里插入图片描述

这时command需要DeadLock.class的monitor,command1需要Object.class的monitor,而两者都不能释放自身持有的monitor,导致陷入死锁

多个线程造成死锁的情况

如果多个线程之间的依赖关系是环形,存在环形的锁依赖关系,那么依然可能发生死锁

在这里插入图片描述

也就是两个线程死锁的扩展情况,当线程1持有锁A需要锁B----线程2持有锁B需要锁C----线程3持有锁C需要锁A时就形成了死锁.

死锁的影响

死锁的影响在不同系统中是不一样的,这取决于系统对死锁的处理能力

数据库

部分数据库可以检测并指定某个线程放弃事务,最后再恢复事务继续处理.

information-schema-innodb-trx-table

TRX_WEIGHT

The weight of a transaction, reflecting (but not necessarily the exact count of) the number of rows altered and the number of rows locked by the transaction. To resolve a deadlock, InnoDB selects the transaction with the smallest weight as the “victim” to roll back. Transactions that have changed nontransactional tables are considered heavier than others, regardless of the number of altered and locked rows.
-- 事务的权重,反映(但不一定是准确的计数)更改的行数和事务锁定的行数。为了解决死锁问题,InnoDB选择权值最小的事务作为“受害者”进行回滚。更改了非事务性表的事务被认为比其他事务更重,而不管更改和锁定的行数是多少

Mysql

-- A
BEGIN;
update users set lastUpdate = NOW() where id =1;
update t2 set `name`='test' where id =1;

-- B
begin ;
update t2 set `name`='test' where id =1;
update users set lastUpdate = NOW() where id =1;

-- 输出
update users set lastUpdate = NOW() where id =1
> 1213 - Deadlock found when trying to get lock; try restarting transaction
> 时间: 4.624s

JVM

无法自动处理

1.几率不高,但是危害很大.

2.一旦发生,多是高并发场景,影响的用户多.

3.根据死锁代码作用的不同,可能会造成性能降低、子系统崩溃、整个系统崩溃的情况.

4.压力测试无法找出所有潜在的死锁

发生死锁的例子

简单的死锁

public class DeadLock {
    public static void main(String[] args) throws InterruptedException {
        new Thread(DeadLock::command).start();
        new Thread(DeadLock::command1).start();
    }
    static void command() {
        synchronized (Object.class) {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (DeadLock.class) {

            }
        }
        System.out.println("command_over");
    }
    static void command1() {
        synchronized (DeadLock.class) {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (Object.class) {

            }
        }
        System.out.println("command1_over");
    }
}

在这里插入图片描述

这时command需要DeadLock.class的monitor,command1需要Object.class的monitor,而两者都不能释放自身持有的monitor,导致陷入死锁

对于退出信号

Process finished with exit code 0为正常退出

Process finished with exit code -1而非0的情况就是不正常的退出.

转账

需要两把锁,一把锁扣钱的,一把锁加钱的,都是对数据的修改,类似于a++如果非原子性,可能会出现使用过期数据的情况

public class TransferMoney {
    public static void main(String[] args) throws InterruptedException {
        Account from = new Account(500);
        Account to = new Account(500);
        Thread first = new Thread(() -> transferMoney(from, to, 200));
        Thread second = new Thread(() -> transferMoney(to, from, 200));
        first.start();
        second.start();
        first.join();
        second.join();
    }

    static void transferMoney(Account from, Account to, int amount) {
        synchronized (from) {
            try {
//                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {
                e.printStackTrace();
            }
            synchronized (to) {
                if (from.balance - amount < 0) {
                    System.out.println("转账失败,余额不足");
                    return;
                }
                from.balance -= amount;
                to.balance += amount;
                System.out.println("成功转账:" + amount + "元");
            }
        }
    }

    public static class Account {
        int balance;

        public Account(int balance) {
            this.balance = balance;
        }
    }
}

和简单死锁类似

多人随机转账

public class MultiTransferMoney {
    static int NUM_ACCOUNTS = 500;
    static int NUM_MONEY = 1000;
    static int NUM_THREADS = 10;
    static int NUM_ITERATIONS = 1000000;
    static AtomicInteger count = new AtomicInteger();

    public static void main(String[] args) {
        List<Account> accounts = new Random().ints(NUM_ACCOUNTS)
                .mapToObj(it -> new Account(NUM_MONEY))
                .collect(Collectors.toList());
        Random random = new Random();
        for (int i = 0; i < NUM_THREADS; i++) {
            new Thread(() -> command(accounts, random)).start();
        }
    }

    static void command(List<Account> accounts, Random random) {
        for (int i = 0; i < NUM_ITERATIONS; i++) {
            Account from = accounts.get(random.nextInt(NUM_ACCOUNTS));
            Account to = accounts.get(random.nextInt(NUM_ACCOUNTS));
            int amount = random.nextInt(NUM_MONEY);
            TransferMoney.transferMoney(from, to, amount);
            System.out.println("当前总转账次数"+count.incrementAndGet());
        }
    }
}
成功转账:71元
当前总转账次数139487
成功转账:674元
当前总转账次数139488
转账失败,余额不足
当前总转账次数139489
成功转账:402元
当前总转账次数139490
成功转账:774元
当前总转账次数139491
成功转账:224元
当前总转账次数139492
转账失败,余额不足
当前总转账次数139493

Process finished with exit code -1

在这里插入图片描述

红色背景的代表两者死锁了.可以看到,虽然随着账户数变大,导致死锁的几率降低了,但是一旦发生死锁,因为是高并发情况,将迅速导致整个服务的崩溃

死锁发生的四个必要条件

1.资源具有互斥性,一次只能有一个线程持有

2.保持本互斥资源的同时还额外请求互斥的资源

3.无法互斥资源无法被其它机制剥夺(正常运行时)

4.多个互斥资源的持有者构成循环等待(互斥资源的释放)

死锁的发送上述四个必要条件缺一不可.

如何定位死锁

通过命令行

JPS java process show

C:\Users\87990>jps
15472 Launcher
16336 Jps
34724 DeadLock
53416

jstack 34724 //获取当前java进程dump信息

两个线程
Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x000000001a880b68 (object 0x0000000780f806f8, a java.lang.Class),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x000000001a8834a8 (object 0x00000007810c8970, a java.lang.Class),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
        at com.example.deadLock.DeadLock.command1(DeadLock.java:32)
        #等待的锁
        - waiting to lock <0x0000000780f806f8> (a java.lang.Class for java.lang.Object)
        #已锁定的锁
        - locked <0x00000007810c8970> (a java.lang.Class for com.example.deadLock.DeadLock)
        at com.example.deadLock.DeadLock$$Lambda$2/1078694789.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)
"Thread-0":
        at com.example.deadLock.DeadLock.command(DeadLock.java:19)
        #等待的锁
        - waiting to lock <0x00000007810c8970> (a java.lang.Class for com.example.deadLock.DeadLock)
        #已锁定的锁
        - locked <0x0000000780f806f8> (a java.lang.Class for java.lang.Object)
        at com.example.deadLock.DeadLock$$Lambda$1/1324119927.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.
多个线程
//多用户随机目标转账
public class MultiTransferMoney {
    static int NUM_ACCOUNTS = 500;
    static int NUM_MONEY = 1000;
    static int NUM_THREADS = 10;
    static int NUM_ITERATIONS = 1000000;
    static AtomicInteger count = new AtomicInteger();

    public static void main(String[] args) {
        List<Account> accounts = new Random().ints(NUM_ACCOUNTS)
                .mapToObj(it -> new Account(NUM_MONEY))
                .collect(Collectors.toList());
        Random random = new Random();
        for (int i = 0; i < NUM_THREADS; i++) {
            new Thread(() -> command(accounts, random)).start();
        }
    }

    static void command(List<Account> accounts, Random random) {
        for (int i = 0; i < NUM_ITERATIONS; i++) {
            Account from = accounts.get(random.nextInt(NUM_ACCOUNTS));
            Account to = accounts.get(random.nextInt(NUM_ACCOUNTS));
            int amount = random.nextInt(NUM_MONEY);
            TransferMoney.transferMoney(from, to, amount);
            System.out.println("当前总转账次数"+count.incrementAndGet());
        }
    }
}
//TransferMoney#transferMoney
    static void transferMoney(Account from, Account to, int amount) {
        synchronized (from) {
            try {
//                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {
                e.printStackTrace();
            }
            synchronized (to) {
                if (from.balance - amount < 0) {
                    System.out.println("转账失败,余额不足");
                    return;
                }
                from.balance -= amount;
                to.balance += amount;
                System.out.println("成功转账:" + amount + "元");
            }
        }
    }
#陷入阻塞
"Thread-9" #21 prio=5 os_prio=0 tid=0x000000001da28800 nid=0x6dac waiting for monitor entry [0x000000001ec8f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.example.deadLock.TransferMoney.transferMoney(TransferMoney.java:22)
        - waiting to lock <0x000000078400dfd0> (a com.example.deadLock.TransferMoney$Account)
        - locked <0x0000000784007628> (a com.example.deadLock.TransferMoney$Account)
      

"Thread-8" #20 prio=5 os_prio=0 tid=0x000000001d9da000 nid=0x88dc waiting for monitor entry [0x000000001ea8f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.example.deadLock.TransferMoney.transferMoney(TransferMoney.java:16)
        - waiting to lock <0x0000000783f982c0> (a com.example.deadLock.TransferMoney$Account)
       

"Thread-7" #19 prio=5 os_prio=0 tid=0x000000001d9d9800 nid=0xd1a8 waiting for monitor entry [0x000000001e98e000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.example.deadLock.TransferMoney.transferMoney(TransferMoney.java:22)
        - waiting to lock <0x000000078400dfd0> (a com.example.deadLock.TransferMoney$Account)
        - locked <0x000000078400db48> (a com.example.deadLock.TransferMoney$Account)
       
"Thread-6" #18 prio=5 os_prio=0 tid=0x000000001d9d8800 nid=0xcf30 waiting for monitor entry [0x000000001e88f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.example.deadLock.TransferMoney.transferMoney(TransferMoney.java:22)
        - waiting to lock <0x000000078400dfd0> (a com.example.deadLock.TransferMoney$Account)
        - locked <0x000000078401e4a0> (a com.example.deadLock.TransferMoney$Account)
       

"Thread-5" #17 prio=5 os_prio=0 tid=0x000000001d9c3800 nid=0xcb2c waiting for monitor entry [0x000000001e78f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.example.deadLock.TransferMoney.transferMoney(TransferMoney.java:22)
        - waiting to lock <0x000000078400dfd0> (a com.example.deadLock.TransferMoney$Account)
        - locked <0x000000078400dba8> (a com.example.deadLock.TransferMoney$Account)
        
"Thread-4" #16 prio=5 os_prio=0 tid=0x000000001d9c3000 nid=0x59d0 waiting for monitor entry [0x000000001e68f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.example.deadLock.TransferMoney.transferMoney(TransferMoney.java:22)
        - waiting to lock <0x000000078400db48> (a com.example.deadLock.TransferMoney$Account)
        - locked <0x000000078401e2b0> (a com.example.deadLock.TransferMoney$Account)
       

"Thread-3" #15 prio=5 os_prio=0 tid=0x000000001d9c0000 nid=0xcab0 waiting for monitor entry [0x000000001e58f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.example.deadLock.TransferMoney.transferMoney(TransferMoney.java:22)
        - waiting to lock <0x0000000783f982c0> (a com.example.deadLock.TransferMoney$Account)
        - locked <0x0000000783fe7430> (a com.example.deadLock.TransferMoney$Account)
       

"Thread-2" #14 prio=5 os_prio=0 tid=0x000000001d9bb800 nid=0xab08 waiting for monitor entry [0x000000001e48e000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.example.deadLock.TransferMoney.transferMoney(TransferMoney.java:16)
        - waiting to lock <0x000000078400db48> (a com.example.deadLock.TransferMoney$Account)
       

"Thread-1" #13 prio=5 os_prio=0 tid=0x000000001d9ba800 nid=0xcf9c waiting for monitor entry [0x000000001e38e000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.example.deadLock.TransferMoney.transferMoney(TransferMoney.java:22)
        - waiting to lock <0x000000078400db48> (a com.example.deadLock.TransferMoney$Account)
        - locked <0x0000000783f982c0> (a com.example.deadLock.TransferMoney$Account)
       

"Thread-0" #12 prio=5 os_prio=0 tid=0x000000001d9ba000 nid=0xd3bc waiting for monitor entry [0x000000001e28e000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.example.deadLock.TransferMoney.transferMoney(TransferMoney.java:22)
        - waiting to lock <0x000000078400dba8> (a com.example.deadLock.TransferMoney$Account)
        - locked <0x000000078400dfd0> (a com.example.deadLock.TransferMoney$Account)
       
#陷入死锁
Found one Java-level deadlock:
=============================
"Thread-9":
  waiting to lock monitor 0x000000001da6ffa8 (object 0x000000078400dfd0, a com.example.deadLock.TransferMoney$Account),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x000000001da6e4c8 (object 0x000000078400dba8, a com.example.deadLock.TransferMoney$Account),
  which is held by "Thread-5"
"Thread-5":
  waiting to lock monitor 0x000000001da6ffa8 (object 0x000000078400dfd0, a com.example.deadLock.TransferMoney$Account),
  which is held by "Thread-0"

Java stack information for the threads listed above:
===================================================
"Thread-9":
        at com.example.deadLock.TransferMoney.transferMoney(TransferMoney.java:22)
        - waiting to lock <0x000000078400dfd0> (a com.example.deadLock.TransferMoney$Account)
        - locked <0x0000000784007628> (a com.example.deadLock.TransferMoney$Account)

"Thread-0":
        at com.example.deadLock.TransferMoney.transferMoney(TransferMoney.java:22)
        - waiting to lock <0x000000078400dba8> (a com.example.deadLock.TransferMoney$Account)
        - locked <0x000000078400dfd0> (a com.example.deadLock.TransferMoney$Account)

"Thread-5":
        at com.example.deadLock.TransferMoney.transferMoney(TransferMoney.java:22)
        - waiting to lock <0x000000078400dfd0> (a com.example.deadLock.TransferMoney$Account)
        - locked <0x000000078400dba8> (a com.example.deadLock.TransferMoney$Account)


Found 1 deadlock.

可以看到多个线程同时永久阻塞并不需要太多线程陷入死锁.可见死锁的危害.

通过ThreadMXBean

/**
 * 通过ThreadMXBean检测死锁
 */
public class ThreadMXBeanDetection {
    static ThreadMXBean threadMXBean;

    public static void main(String[] args) throws InterruptedException {
        new Thread(ThreadMXBeanDetection::command).start();
        new Thread(ThreadMXBeanDetection::command1).start();
        //等待死锁发生
        TimeUnit.SECONDS.sleep(3);
        threadMXBean = ManagementFactory.getThreadMXBean();
        long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();
        if (deadlockedThreads != null && deadlockedThreads.length > 0) {
            Arrays.stream(deadlockedThreads).forEach(ThreadMXBeanDetection::deadlockHandle);
        }
    }
    //报警/日志/重启程序,及时发现死锁并解决
    static void deadlockHandle(long id) {
        ThreadInfo threadInfo = threadMXBean.getThreadInfo(id);
        System.out.println("threadName:"+threadInfo.getThreadName()+"___was deadlock");
    }
    static void command() {
        synchronized (Object.class) {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (DeadLock.class) {

            }
        }
        System.out.println("command_over");
    }
    static void command1() {
        synchronized (DeadLock.class) {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (Object.class) {

            }
        }
        System.out.println("command1_over");
    }
}
threadName:Thread-1___was deadlock
threadName:Thread-0___was deadlock

修复死锁的策略

处理流程

目标

由于死锁一旦发生,影响可能迅速波及其它线程/服务,造成损失.所以一定要防范于未然.

临时处理

保存当前堆栈的dump文件,然后迅速重启服务避免影响用户体验

后续处理

暂时保证服务安全后,利用服务堆栈信息,排查死锁、修改代码、重新发布.

常见策略

1.避免策略

在处理逻辑实际上不在乎获取锁的顺序时,避免相反的获取锁的顺序.

如果有自增的主键,就能更方便的处理顺序问题

public class MultiTransferMoney {
    static int NUM_ACCOUNTS = 500;
    static int NUM_MONEY = 1000;
    static int NUM_THREADS = 10;
    static int NUM_ITERATIONS = 1000000;
    static AtomicInteger count = new AtomicInteger();

    public static void main(String[] args) {
        List<Account> accounts = new Random().ints(NUM_ACCOUNTS)
                .mapToObj(it -> new Account(NUM_MONEY))
                .collect(Collectors.toList());
        Random random = new Random();
        for (int i = 0; i < NUM_THREADS; i++) {
            new Thread(() -> command(accounts, random)).start();
        }
    }

    static void command(List<Account> accounts, Random random) {
        for (int i = 0; i < NUM_ITERATIONS; i++) {
            Account from = accounts.get(random.nextInt(NUM_ACCOUNTS));
            Account to = accounts.get(random.nextInt(NUM_ACCOUNTS));
            int amount = random.nextInt(NUM_MONEY);
//            TransferMoney.transferMoney(from, to, amount);
            TransferMoney.safeTransferMoney(from, to, amount);
            System.out.println("当前总转账次数"+count.incrementAndGet());
        }
    }
}

    static Object lock = new Object();
    //safe
    static void safeTransferMoney(Account first, Account second, int amount) {
        int firstIdentity = System.identityHashCode(first);
        int secondIdentity = System.identityHashCode(second);
        Object firstLock = firstIdentity > secondIdentity ? first : second;
        Object secondLock = firstIdentity > secondIdentity ? second : first;
        if (firstIdentity == secondIdentity) {
            //避免hash碰撞的问题
            synchronized (lock) {
                synchronized (firstLock) {
                    synchronized (secondLock) {
                        if (first.balance - amount < 0) {
                            System.out.println("转账失败,余额不足");
                            return;
                        }
                        first.balance -= amount;
                        second.balance += amount;
                        System.out.println("成功转账:" + amount + "元");
                    }
                }
            }
        }else{
            synchronized (firstLock) {
                synchronized (secondLock) {
                    if (first.balance - amount < 0) {
                        System.out.println("转账失败,余额不足");
                        return;
                    }
                    first.balance -= amount;
                    second.balance += amount;
                    System.out.println("成功转账:" + amount + "元");
                }
            }
        }
    }

    //unsafe
    static void transferMoney(Account from, Account to, int amount) {
        synchronized (from) {
            synchronized (to) {
                if (from.balance - amount < 0) {
                    System.out.println("转账失败,余额不足");
                    return;
                }
                from.balance -= amount;
                to.balance += amount;
                System.out.println("成功转账:" + amount + "元");
            }
        }
    }

实际工程中避免死锁

1.设置超时时间

public class DeadLock {
    static ReentrantLock lock1 = new ReentrantLock();
    static ReentrantLock lock2 = new ReentrantLock();
    static Random random=new Random();
    public static void main(String[] args) throws InterruptedException {
//        new Thread(DeadLock::command).start();
//        new Thread(DeadLock::command1).start();
//        System.out.println(random.nextInt(2000));
        new Thread(DeadLock::lock1).start();
        new Thread(DeadLock::lock2).start();

    }

    static void lock1() {
        for (int i = 0; i < 100; i++) {
            try {
                if (lock1.tryLock(800, TimeUnit.MILLISECONDS)) {
                    System.out.println(Thread.currentThread().getName() +"获取锁1成功");
                    Thread.sleep(random.nextInt(1000));
                    if (lock2.tryLock(1300, TimeUnit.MILLISECONDS)) {
                        Thread.sleep(random.nextInt(1000));
                        System.out.println(Thread.currentThread().getName()+"获取锁2成功");
                        lock1.unlock();
                        lock2.unlock();
                        return;
                    } else {
                        System.out.println(Thread.currentThread().getName()+"获取锁2失败");
                        lock1.unlock();
                    }
                } else {
                    System.out.println(Thread.currentThread().getName()+"获取锁1失败");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    static void lock2() {
        for (int i = 0; i < 100; i++) {
            try {
                if (lock2.tryLock(800, TimeUnit.MILLISECONDS)) {
                    Thread.sleep(random.nextInt(1000));
                    System.out.println(Thread.currentThread().getName() +"获取锁2成功");
                    if (lock1.tryLock(1300, TimeUnit.MILLISECONDS)) {
                        Thread.sleep(random.nextInt(1000));
                        System.out.println(Thread.currentThread().getName()+"获取锁1成功");
                        lock1.unlock();
                        lock2.unlock();
                        return;
                    } else {
                        System.out.println(Thread.currentThread().getName()+"获取锁1失败");
                        lock2.unlock();
                    }
                } else {
                    System.out.println(Thread.currentThread().getName()+"获取锁2失败");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
Thread-0获取锁1成功
Thread-1获取锁2成功
Thread-0获取锁2失败
Thread-0获取锁1成功
Thread-1获取锁1失败
Thread-1获取锁2成功
Thread-0获取锁2失败
Thread-0获取锁1成功
Thread-1获取锁1失败
Thread-1获取锁2成功
Thread-0获取锁2失败
Thread-0获取锁1成功
Thread-1获取锁1失败
Thread-1获取锁2成功
Thread-0获取锁2失败
Thread-0获取锁1成功
Thread-1获取锁1失败
Thread-1获取锁2成功
Thread-0获取锁2失败
Thread-0获取锁1成功
Thread-1获取锁1失败
Thread-1获取锁2成功
Thread-1获取锁1失败
Thread-1获取锁2成功
Thread-0获取锁2失败
Thread-1获取锁1成功
Thread-0获取锁1成功
Thread-0获取锁2成功

Process finished with exit code 0

2.多使用并发类而不是自己设计锁

如java.util.concurrent.atomic、ReentrantLock、ConcurrentHashMap、ConcurrentLinkedQueue

3.多用并发集合而不是同步集合,因为并发集合比同步集合的扩展性更好.

例如ConcurrentHashMap与Collections.SynchronizedMap.

前者使用分段锁和Unsafe类的CAS操作,锁的粒度更细,后者则是各个方法内部代码块同一添加添加synchronized.

4.尽量降低锁的使用粒度:用不同的锁而不是同一个锁

尽量减少单个锁锁定的范围,例如读写锁(ReentrantReadWriteLock).

5.能使用同步代码块,就不要使用同步方法

除了可以指定锁对象外,代码块相对而言粒度更细

6.给线程起有意义的名字

这样debug和排查时事半功倍
在这里插入图片描述

7.避免锁的嵌套

8.可以先通过调用链计算锁的分配是否会造成死锁

9.专锁专用,不通锁分担不同功能

活锁

原因

重试机制固定,导致获取锁的冲突总是发生

解决

以太网的指数退避算法

因为共用网线,导致数据的IO发送碰撞,当发送碰撞后,从0-1023中随机选择一个数字作为等待时间

工程中的活锁

消息队列

  • 1.消息如果处理失败,就放在队列头进行重试

  • 2.由于消息依赖的服务出现问题,处理该消息一直失败

  • 3.虽然没有阻塞,但程序只是一直在空转

在这里插入图片描述

解决:

1.由于消息可能短时间无法恢复,所以可以将需要重试的消息重新加入队列(放到队尾),而不是反复重试.

2.添加重试数量的限制(一般3-5次),之后写入数据库,触发报警机制,再通过额外的定时任务进行重试,这样就不会影响到主程序的运行

饥饿

当线程需要某些资源(例如CPU),但却始终无法获得.资源通常指代CPU,因为如内存之类的资源,如果无法获取会出现报错,而CPU却是通过OS进行调度的

当线程优先级过低,或者某些线程持有锁但是却无限循环不释放锁,或者某程序对某文件上了写锁后始终占有

饥饿可能导致响应性变差:例如游览器一个线程渲染页面,另一个线程下载文件,图片之类的,如果下载的线程优先级特别高,将导致页面响应时间延长,用户体验变差.

由于不同OS对于优先级的处理不同,所以为避免饥饿,不应该人为设置线程优先级.同时避免对资源的长期占有(逻辑上对锁使用却不释放的逻辑错误).

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值