死锁:

由于资源竞争或通信问题造成的一个阻塞现象,若无外力作用无法推进下去.
当线程互相持有对方所需要的资源时,会互相等待对方释放资源,如果线程都不主动释放所占有的资源,将产生死锁。

条件

1 < 竞争资源 < 抢占资源的线程数
如果竞争资源为1时,不存在死锁问题,因为不是不是A线程占有就是B线程占有,不会相互等待对方释放

代码演示

import com.enjoy.demo.p1.ch1.class1.SleepTools;

/**
 * @Author: BillYu
 * @Description:演示普通的死锁和解决
 * @Date: Created in 13:29 2019-03-15.
 *
 *
 * 死锁:由于资源竞争或通信问题造成的一个阻塞现象,若无外力作用无法推进下去
 * 1 < 竞争资源 < 抢占资源的线程数
 *
 */
public class NormalDeadLock {
    /**
     * 第一个锁
     */
    private static Object valueFirst = new Object();

    /**
     * 第二个锁
     */
    private static Object valueSecond = new Object();

    /**
     * 先拿第一个锁,再拿第二个锁
     */
    private static  void firstToSecond() throws InterruptedException{
        String threadName = Thread.currentThread().getName();
        synchronized (valueFirst){
            System.out.println(threadName+" get first");
            SleepTools.ms(100);
            synchronized (valueSecond){
                System.out.println(threadName+" get second");
            }
        }
    }

    /**
     * 先拿第二个锁,再拿第一个锁
     */
    private static  void secondToFirst() throws InterruptedException{
        String threadName = Thread.currentThread().getName();
        synchronized (valueSecond){
            System.out.println(threadName+" get second");
            SleepTools.ms(100);
            synchronized (valueFirst){
                System.out.println(threadName+" get first");
            }
        }
    }

    /**
     * 执行先拿第二个锁,再拿第一个锁
     */
    private static class TestThread extends Thread{
        private String name;

        public TestThread(String name){
            this.name = name;
        }
        @Override
        public void run(){
            Thread.currentThread().setName(name);
            try{
                secondToFirst();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        Thread.currentThread().setName("TestDeadLock");
        TestThread testThread = new TestThread("SubTestThread");
        testThread.start();
        try{
            firstToSecond();
        }catch (InterruptedException e){
            e.printStackTrace();
        }

    }
    
}

控制台输出:

SubTestThread get second
TestDeadLock get first

怀疑发生死锁如何排查

通过jps查询应用的id,再通过jstack id查看应用的锁的持有情况(持有的锁 等待的锁)。
image.png
image.png
image.png

解决死锁

1.保证加锁的顺序

比如对象的hashcode,可以使用System.identityHashCode() 系统提供的native方法,两个对象identityHashCode相同的概率只有千万分之一。如果对象的identityHashCode相同,可以再在外面加一个总的锁,如果不同就以identityHashCode从小到大获取锁的顺序进行。

2.ReentrantLock

尝试获取锁,直到获取到所有的锁为止,有锁竞争时释放锁。
可能会导致活锁的情况。可休眠随机时长,避免活锁的发生。

活锁

尝试拿锁的机制中,发生多个线程之间相互谦让,不断发生拿锁,释放锁的过程。
解决办法:每个线程休眠随机时长,错开拿锁的时间