Java JUC并发之对于各种锁的理解以及如何解决死锁问题

对于各种锁的理解

  1. 公平锁、非公平锁

    公平锁 : 不允许插队,必须先来后到

    非公平锁: 允许插队(默认都是非公平锁)

  2. 可重入锁

    synchronized 这里是同一把锁

    package com.liu.lock8.reentrantLock;
    
    public class Demo01 {
    
        public static void main(String[] args) {
    
            Phone phone = new Phone();
            new Thread(()->{
                phone.sendMsg();
            },"A").start();
    
            new Thread(()->{
                phone.sendMsg();
            },"B").start();
        }
    }
    
    class Phone{
    
        public synchronized void sendMsg() {
            System.out.println(Thread.currentThread().getName() + " => sendMsg()");
            call();  // 这里也有锁
        }
    
        public synchronized void call() {
            System.out.println(Thread.currentThread().getName() + " => call()");
        }
    }
    

    lock 这里是两把不同的锁

    package com.liu.lock8.reentrantLock;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Demo02 {
    
        public static void main(String[] args) {
    
            Phone02 phone = new Phone02();
            new Thread(()->{
                phone.sendMsg();
            },"A").start();
    
            new Thread(()->{
                phone.sendMsg();
            },"B").start();
        }
    }
    
    class Phone02{
    
        Lock lock = new ReentrantLock();
    
        public  void sendMsg() {
    
            lock.lock(); // 一共有两把锁
            // lock锁必须配对,否则会死锁
    
            try {
                System.out.println(Thread.currentThread().getName() + " => sendMsg()");
                call();  // 这里也有锁
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public  void call() {
            lock.lock();
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " => call()");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
    

    可重入锁(递归锁)=> 可以递归调用和嵌套

    • 拿到外面的多之后,就可以自动获得里面的锁
    • lock锁必须配对,否则会导致死锁
  3. 自旋锁 spinlock

自己写一个自旋锁

package com.liu.lock8.spinlock;

import java.util.concurrent.atomic.AtomicReference;

/**
 * 自旋锁
 */
public class SpinlockDemo {

    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    // 加锁
    public void myLock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + " ==> mylock");
        // 自旋锁
        while (!atomicReference.compareAndSet(null, thread)) {

            System.out.println(Thread.currentThread().getName() + "自旋中");
        }
    }

    // 解锁
    public void myUnLock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + " ==> myUnlock");

        atomicReference.compareAndSet(thread, null);
    }
}

运行结果: 线程T1先获得自旋锁,T1进入休眠状态,之后线程T2获得自旋锁,而T2一直在自旋,直到T1休眠结束,释放锁,线程T2才停止自旋并解锁。

  1. 死锁

    什么是死锁?

    模拟死锁:

package com.liu.lock8.deadlock;

import java.util.concurrent.TimeUnit;

public class DeadLockDemo {

    public static void main(String[] args) {
        String lockA = "lockA";
        String lockB = "lockB";

        new Thread(new MyThread(lockA,lockB),"T1").start();
        new Thread(new MyThread(lockB,lockA),"T2").start();
    }
}

class MyThread implements Runnable {

    private String lockA; // 这里的锁指的是要锁定的资源类对象
    private String lockB;

    public MyThread(String lockA, String lockB) {
        this.lockA = lockA;
        this.lockB = lockB;
    }

    @Override
    public void run() {

        synchronized (lockA) {
            System.out.println(Thread.currentThread().getName() + " lock: " + lockA +",try to get "+lockB);

            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lockB) {
                System.out.println(Thread.currentThread().getName() + "lock: " + lockB +  ",try to get " + lockA);
            }
        }
    }
}

运行结果: T1获取到了lockA,但是同时想要拿到lockB;

​ T2获取到了lockB,但是同时想要拿到lockA;

两个线程一直在等待对方释放锁,线程无法停止下来,于是便会导致死锁!

解决死锁办法 在IDEA终端上可以使用这些命令排查问题

  1. 使用 jps -l查询运行的进程号

  1. 使用 jstack 进程号 => 查看死锁信息

面试: 工作中如何排查问题(死锁)

  1. 日志
  2. 堆栈 jstack
posted @ 2021-07-19 02:33  夕立君  阅读(51)  评论(0编辑  收藏  举报