JUC:公平锁、非公平锁、共享锁、独占锁、可重入锁、自旋锁、死锁、死锁排查方法


各种锁的理解


1、公平锁、非公平锁

公平锁:非常公平,任何线程获得公平锁,那么就会执行锁中业务直到结束,过程中任何进程都不得干预打扰。不能插队。

非公平锁:在获得非公平锁之后,执行代码过程中,其他线程可以插入执行,该线程暂停执行,等待其他线程执行完毕,才可继续执行。可插队。(默认非公平锁)


2、共享锁、独占锁

共享锁:就是读取锁里的读锁,进行读操作,任何经常都可获得该锁。

独占锁:就是读取锁里的写锁,只允许一个线程获得该锁。


3、可重入锁

可重入锁(递归锁):拿到了外面的锁,内部的锁也都统统拿到了。

lock锁与synchronized锁都是可重入锁,但是有一定的区别。

lock锁必须配对,有上锁对应必须就有解锁,不然会死锁。


4、自旋锁

在这里插入图片描述

上图,CAS的源码分析就是一个自旋锁。自旋锁一定是使用CAS做为底层。因为CAS是C++写的原语操作。


自定义简单的锁

public class MyLock {
    private AtomicReference<Thread> atomicReference = new AtomicReference<>();

    public void lock(){
        // 获取当前执行的线程
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName()+ " => 正在获取自旋锁");
        // CAS操作,自旋锁
        while (!atomicReference.compareAndSet(null,thread)){

        }
    }

    public void unLock(){
        Thread thread = Thread.currentThread();
        // 解锁,将thread赋值为null,上面的自旋锁就可以解除
        atomicReference.compareAndSet(thread,null);
    }
}

class MyLockTest{
    public static void main(String[] args) {
        MyLock myLock = new MyLock();
        
        new Thread(()->{
            myLock.lock();
            try{
                System.out.println("A线程正在执行...");
                TimeUnit.SECONDS.sleep(5);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                System.out.println("A线程执行完毕!");
                myLock.unLock();
            }
        },"A").start();

        new Thread(()->{
            myLock.lock();
            try{
                System.out.println("B线程正在执行...");
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                System.out.println("B线程执行完毕!");
                myLock.unLock();
            }
        },"B").start();
    }
}

输出:

A => 正在获取自旋锁
A线程正在执行...(等5秒)
B => 正在获取自旋锁
A线程执行完毕!
B线程正在执行...
B线程执行完毕!

5、死锁

就是两个线程都持有各自的锁,但是又要获取对方的锁,此时对方的锁都没有释放,那么就会进入死锁状态。

public class DieLock implements Runnable{
    private String lock1;
    private String lock2;

    public DieLock(String lockA, String lockB) {
        this.lock1 = lockA;
        this.lock2 = lockB;
    }

    @Override
    public void run() {
        synchronized (lock1){
            System.out.println(Thread.currentThread().getName() + "要获取" + lock2);
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            synchronized (lock2){
                System.out.println(Thread.currentThread().getName() + "要获取" + lock1);
            }
        }
    }
}

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

        // 注意锁的对象
        new Thread(new DieLock(lockA,lockB),"1 ").start();
        new Thread(new DieLock(lockB,lockA),"2 ").start();
    }
}

输出:

1 要获取LockB
2 要获取LockA
(程序进入无限死锁状态...)

死锁排查 jps

除了看日志,还可以看堆栈信息

  • 可在Terminal下输入jps -l (java process)定位正在进行的进程号:

在这里插入图片描述

  • 查询到编号为13860的进程,是DieTest类的。可通过jstack 13860(java stack)查看具体信息:

在这里插入图片描述

可查到详细信息,两个进程互相等待对方的锁。


关于进程、线程、并发、并行的问题

  1. Java中默认有几个线程?

    默认2个线程,一个main线程,一个GC线程

  2. Java真的能开启线程吗?

    不能。

    通过源码private native void start0();分析,java只能通过本地方法去调用开启线程。由底层c语言区操作的。

  3. 并发与并行的区别?

    并发是多个线程同一时间段共同执行一个资源;

    并行是多个线程同一时刻同行。

原文地址:https://www.cnblogs.com/turbo30/p/13688199.html