码迷,mamicode.com
首页 > 其他好文 > 详细

关于各种锁的理解

时间:2021-05-04 15:15:22      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:lock   unsafe   命令   修改   占用   system   art   getname   second   

公平锁,非公平锁

公平锁:非常公平,不能插队,必须先来后到

//参数写为true,就表示公平锁(不写默认就是非公平锁)
public ReentrantLock(boolean fair) {
     sync = fair ? new FairSync() : new NonfairSync();
 }

非公平锁:非常不公平,允许插队,可以改变顺序(lock,synchronized默认都是非公平锁)

public ReentrantLock() {
    sync = new NonfairSync();
}

可重入锁

简单理解就是:锁里面还有一个锁。

特性:就是一个线程执行可重入锁后,释放全部锁的时候,另一个线程才能继续执行这个可重入锁。

加上A锁里面还有一把B锁。
线程甲执行完A,线程乙还不能执行A,因为A里面还有一把B锁。
甲执行完B锁就会释放B锁,然后再释放A锁,这时乙才能执行(使用lock这个过程特别明显)

通过Synchronized 锁实现

public class LockDemo {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(()->{
            phone.sms();
        },"A").start();
        new Thread(()->{
            phone.sms();
        },"B").start();
    }
}
class Phone{
    public synchronized void sms(){
        System.out.println(Thread.currentThread().getName()+"=> sms");
        call();// 这里也有一把锁
    }
    public synchronized void call(){
        System.out.println(Thread.currentThread().getName()+"=> call");
    }
}
A=> sms
A=> call
B=> sms
B=> call

Lock 锁实现可重入锁

public class LockDemo {
    public static void main(String[] args) {
        Phone2 phone2  = new Phone2();
        new Thread(()->{
            phone2.sms();
        },"A").start();
        new Thread(()->{
            phone2.sms();
        },"B").start();
    }
}
class Phone2 {
    Lock lock = new ReentrantLock();

    public void sms() {
        lock.lock();//细节:这个两把锁,两个钥匙
//        lock 锁必须配对,否则就是死锁在里面
        try {
            System.out.println(Thread.currentThread().getName() + "=>sms");
            call();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void call() {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "=>call");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
A=>sms
A=>call
B=>sms
B=>call

自旋锁

何谓自旋锁?它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名

自旋锁会不断的尝试,直到成功为主。

其实在Unsafe类中就有自旋锁的使用,下面的源码通过CAS和while循环就实现了自旋锁。

技术图片

示例:自定义实现自旋锁(自旋锁是通过CAS和while循环实现的)

public class SpinlockDemo {
    //使用原子类通过CAS实现自旋锁
    // 默认
    // int 0
    //thread null
    AtomicReference<Thread> atomicReference=new AtomicReference<>();

    //加锁
    public void myLock(){
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName()+"===> mylock");

        //自旋锁 当有一个线程A调用该方法,就将原始值null改为A thread,修改成功就结束自旋
        while (!atomicReference.compareAndSet(null,thread)){
            //System.out.println(Thread.currentThread().getName()+" ==> 自旋中~");
        }
    }


    //解锁
    public void myUnlock(){
        Thread thread=Thread.currentThread();
        System.out.println(thread.getName()+"===> myUnlock");
        atomicReference.compareAndSet(thread,null);
    }

}

public class TestSpinLock {
    public static void main(String[] args) throws InterruptedException {

        //使用CAS实现自旋锁
        SpinlockDemo spinlockDemo=new SpinlockDemo();
        new Thread(()->{
            spinlockDemo.myLock();
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                spinlockDemo.myUnlock();
            }
        },"t1").start();

        TimeUnit.SECONDS.sleep(1);


        new Thread(()->{
            spinlockDemo.myLock();
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                spinlockDemo.myUnlock();
            }
        },"t2").start();
    }
}
t1===> mylock
t2===> mylock
t1===> myUnlock
t2===> myUnlock

//从上面的结果我们看到,t1拿到锁后,一直在自旋,直到thread不等于空结束自旋。t2才能拿到锁,然后自旋,最后解锁

死锁

简单理解:A:先交货再给;B:先给钱再交货

技术图片

死锁示例

public class DeadLock {
    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+"===>get"+lockB);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lockB){
                System.out.println(Thread.currentThread().getName()+" lock"+lockB+"===>get"+lockA);
            }
        }
    }
}

如何排查死锁

  1. 运行程序造成死锁后,打开idea Terminal控制台。

  2. 输入使用 jps 定位进程号(jdk 目录 bin 下 :有一个 jps)

    命令: jps -l 查看所有进程号

    技术图片

  3. 使用 jstack 进程进程号找到死锁信息

    技术图片

技术图片

推荐文章

参考教程:https://www.kuangstudy.com/

关于各种锁的理解

标签:lock   unsafe   命令   修改   占用   system   art   getname   second   

原文地址:https://www.cnblogs.com/lanxinren/p/14724629.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!