• 18、各种锁的理解(非公平锁和公平锁、可重入锁、自旋锁、死锁)


    非公平锁和公平锁

    非公平锁:非常不公平的锁,效率高!(Lock和synchronized 默认是这个锁)

    公平锁:非常公平的锁,遵循先来后到的原则!

    比如:有两个线程耗时 3h 3s,公平锁回去等待3h后在执行3s

    非公平锁会直接执行3s

    怎么创建呢?

    public ReentrantLock() {    // 默认非公平锁
        sync = new NonfairSync();
    }
    
    public ReentrantLock(boolean fair) {    // 设置为true,就是公平锁
        sync = fair ? new FairSync() : new NonfairSync();
    }

    可重入锁

    可重入锁(也叫递归锁)

    介绍

     代码测试

    synchronized版

    package com.zxh.lock;
    
    public class Demo01 {
        public static void main(String[] args) {
            Phone phone = new Phone();
    
            new Thread(()->{
                phone.msg();
            }, "A").start();
            new Thread(()->{
                phone.msg();
            }, "B").start();
    
        }
    }
    class Phone{
    
        public synchronized void msg(){
            System.out.println(Thread.currentThread().getName() + " msg()");
            call();
        }
    
        public  synchronized void call(){
            System.out.println(Thread.currentThread().getName() + "call()");
        }
    }

    Lock版

    package com.zxh.lock;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Demo02 {
        public static void main(String[] args) {
            Phone2 phone = new Phone2();
    
            new Thread(()->{
                phone.msg();
            }, "A").start();
            new Thread(()->{
                phone.msg();
            }, "B").start();
    
        }
    }
    class Phone2{
        Lock lock = new ReentrantLock();
    
        // 可重入锁,拿到msg的锁,相对的就拿到了call的锁
        public void msg(){
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " msg()");
                call();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void call(){
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "call()");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    自旋锁

    CAS底层就是调用自旋锁

    自定义自旋锁

    package com.zxh.lock;
    
    import org.omg.CORBA.TIMEOUT;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicReference;
    
    public class SpinLock {
    
        // Thread泛型,内存中的默认值为null
        private AtomicReference<Thread> atomicReference = new AtomicReference();
    
        // 自定义加锁方法
        public void mylock(){
            boolean flag;
            Thread thread = Thread.currentThread(); // 获取调用该锁的线程
            System.out.println(thread.getName() + "=> mylock");
    
            // 自旋锁定义,如果我们期望 当前的原子引用的值为null,就修改为当前线程thread
            /*
                线程A和B用的是同一把锁
                A进入,atomicReference.compareAndSet(null, thread),因为一开始为null,所以替换内存中值为A线程
                    替换成功返回true,没有进入循环
                B进入,atomicReference.compareAndSet(null, thread),因为已经有了A进程的值
                    所以替换失败返回false,进入死循环
                需要等到A线程解锁,调用atomicReference.compareAndSet(thread, null);方法,将值设置null
                B线程才可以停止循环,再执行解锁操作
              */
            while(!atomicReference.compareAndSet(null, thread)){
    
            }
        }
    
        // 自定义解锁方法
        public void myunlock(){
            Thread thread = Thread.currentThread();
            System.out.println(thread.getName() + "=> myunlock");
    
            // 将当前的原子引用的值(内存中的值)修改为null
            // 因为修改为null之后,mylock方法中的 compareAndSet方法就会修改成功
            atomicReference.compareAndSet(thread, null);
        }
    
    }

    测试代码

    package com.zxh.lock;
    
    import java.util.concurrent.TimeUnit;
    
    public class TestSpinLock {
        public static void main(String[] args) throws InterruptedException {
            SpinLock spinLock = new SpinLock();
    
            new Thread(()->{
                spinLock.mylock();
    
                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                spinLock.myunlock();
            },"A").start();
    
            TimeUnit.SECONDS.sleep(1);
    
            new Thread(()->{
                spinLock.mylock();
    
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                spinLock.myunlock();
            },"B").start();
    
    
        }
    }

    死锁

    为什么会发生死锁?

    比如:两个线程互相抢夺对方的资源

    死锁测试,怎么排除死锁:

    package com.zxh.lock;
    
    import java.util.concurrent.TimeUnit;
    
    public class DeadLockDemo {
        public static void main(String[] args) {
            String lockA = "苹果";
            String lockB = "蛋糕";
    
            new Thread(new MyThread(lockA, lockB), "A").start();
            new Thread(new MyThread(lockB, lockA), "B").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() + "=> " + lockA + " 又想要 " + lockB);
    
                synchronized (lockB){
                    System.out.println(Thread.currentThread().getName() + "=> " + lockB + "又想要 " + lockA);
    
                }
    
            }
        }
    }

    因为死锁没有输出,如何排错?

    1、使用jps -l定位正在运行的进程号

     

    2、使用jstack 进程号找到死锁问题

     

     

     面试,工作中! 排查问题:通过 1、日志 2、堆栈信息

     

    致力于记录学习过程中的笔记,希望大家有所帮助(*^▽^*)!
  • 相关阅读:
    几个比较好的IT站和开发库官网
    Win7下Qt5.2中使用OpenGL的glu函数库无法使用的解决方案
    QT5.2 Assistant-设置应用程序图标
    linux下文件编码格式转换方法(gb18030/utf-8)
    QT-进制转换计算器
    QT-图标设置
    QT-make: *** No rule to make target
    QT的exe文件打开显示,无法定位程序***输入点于动态链接库****
    QT工程文件上传Github仓库
    Eclipse中文乱码
  • 原文地址:https://www.cnblogs.com/zxhbk/p/13031903.html
Copyright © 2020-2023  润新知