zoukankan      html  css  js  c++  java
  • 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、堆栈信息

     

    致力于记录学习过程中的笔记,希望大家有所帮助(*^▽^*)!
  • 相关阅读:
    mac 系统安装selenium注意事项
    (mac系统下)mysql 入门
    Mac系统下配置JAVA Maven Ant 环境变量
    解决mac 下mysql安装后root用户登录密码错误问题
    关于使用eclipse maven UpdateProject时报错,无法更新本地仓库的问题解决方案
    201920201学期20192403《网络空间安全专业导论》第一周学习总结
    与数值相关的全局方法 菜鸟
    Jquery插件开发 菜鸟
    Jquery对象和DOM对象Jquery API (1) 菜鸟
    新的一年,新的生活
  • 原文地址:https://www.cnblogs.com/zxhbk/p/13031903.html
Copyright © 2011-2022 走看看