可重入锁和LockSupport相关

代码块方式

public class ReEnterLockDemo {
    static Object objectLockA = new Object();

    public static void m1() {
        new Thread(() -> {
            synchronized (objectLockA) {
                System.out.println(Thread.currentThread().getName() + "	" + "---外层调用");
                synchronized (objectLockA) {
                    System.out.println(Thread.currentThread().getName() + "	" + "---外层调用");
                    synchronized (objectLockA) {
                        System.out.println(Thread.currentThread().getName() + "	" + "---外层调用");
                    }
                }
            }
        },"t1").start();
    }


    public static void main(String[ ]args ) {
        m1();
    }
}

代码方法:

public class ReEnterLockDemo1 {


    public synchronized  void m1(){
        System.out.println("----外层");
        m2();
    }

    public synchronized  void m2(){
        System.out.println("----中层");
        m3();
    }

    public synchronized  void m3(){
        System.out.println("----内层");

    }
    public static void main(String args[]) {
        ReEnterLockDemo1 reEnterLockDemo1 = new ReEnterLockDemo1();
        reEnterLockDemo1.m1();
    }
}

wait 和notify

public class LockSupportDemo {
    static Object objectLock = new Object();
    public static void main(String[] args) {
        new Thread( () -> {
            synchronized (objectLock) {
                System.out.println(Thread.currentThread().getName() +"	" +"----come in");

                try {
                    objectLock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() +"	" +"---- 被唤醒");
            }
        },"A").start();

        new Thread( () -> {
            synchronized (objectLock) {
                System.out.println(Thread.currentThread().getName() +"	" +"----被通知");

                try {
                    objectLock.notify();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}


结论:1.wait方法和notify方法,两个都需要同步代码块,否则会抛异常
   2. 将notify放在wait方法前面,程序无法执行,无法唤醒

lock 实例

public class LockSupportDemo1 {
    static Lock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();
    public static void main(String[] args) {
        new Thread( () -> {
            lock.lock();
            System.out.println(Thread.currentThread().getName() +"	" +"----come in");
            try {
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() +"	" +"---- 被唤醒");
            lock.unlock();

        },"A").start();

        new Thread( () -> {
            lock.lock();
            try {
                condition.signal();
                System.out.println(Thread.currentThread().getName() +"	" +"---- 通知");
            } catch (Exception e) {
                e.printStackTrace();
            }
            lock.unlock();
        },"B").start();
    }
}

LockSupport的支持用法

public class LockSupportDemo2 {

    public static void main(String[] args) {
        Thread a = new Thread( () -> {

            System.out.println(Thread.currentThread().getName() +"	" +"----come in");
            LockSupport.park();
            System.out.println(Thread.currentThread().getName() +"	" +"---- 被唤醒");
            },"A");
            a.start();

        Thread b = new Thread( () -> {
            LockSupport.unpark(a);
            System.out.println(Thread.currentThread().getName() +"	" +"----通知了");
         },"B");
         b.start();
    }
}

 结论:

LockSupport调用的Unsafe中的native代码

LockSupport提供park() 和unpark()方法实现阻塞线程和解除线程阻塞的过程。

LockSupport和每个使用它的线程独有一个许可(permit)关联,permit相当于1,0的开关,默认是0。

调用一次unpark就加1变成1,调用一次park会消费permit,也就是将1变成0,同时park立即返回。

如再次调用park会变成阻塞(因为permit为零了会阻塞在这里,一直到permit变成1),这时调用unpark

会把permit置为1.

每个线程都有一个相关的permit,permit最多只有1个,重复调用unpark也不会阻塞凭证

形象的理解
线程阻塞需要消耗凭证(permit),这个凭证最多只有1个

当调用park方法时

如果有凭证,则会直接消耗掉这个凭证然后正常退出,

如果无凭证就必须阻塞等待凭证可用。

而unpark则相反,它会增加一个凭证,但凭证最多只能有1个,累加无效。

面试题:

1.为什么可以唤醒线程后阻塞线程?

因为unpark获得了一个凭证,之后再调用park方法,就可以名正言顺的凭证消费,故不会阻塞。

2. 为什么唤醒两次后阻塞两次,但最终结果还会阻塞线程?

因为凭证的数量最多为1,连续调用两次unpark和调用一次unpark效果一样,只会增加一个凭证,

而调用两次park却需要消费两个凭证,证不够,不能放行。

--摘自 周阳老师

原文地址:https://www.cnblogs.com/liuyi13535496566/p/14433448.html