JUC

可重入锁:

可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提,锁对象得是同一个对象),不会因为之前已经获取过还没释放而阻塞。Java中 ReentrantLock 和 synchronized 都是可重入锁,可重入锁的一个优点是可一定程度避免死锁。

可重入锁的种类:

隐式锁(即synchronized关键字使用的锁)默认是可重入锁。

Synchronized的重入的实现机理。

每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针。

当执行monitorenter时,如果目标锁对象的计数器为零,那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加1。

在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程是当前线程,那么Java虚拟机可以将其计数器加1,否则需要等待,直至持有线程释放该锁。

当执行monitorexit时,Java虚拟机则需将锁对象的计数器减1。计数器为零代表锁已被释放。

显式锁(即Lock)也有ReentrantLock这样的可重入锁。

LockSupport:

用于创建锁和其他同步类的基本线程阻塞原语。官网

LockSupport中的 park()unpark() 的作用分别是阻塞线程和解除阻塞线程

三种让线程等待唤醒的方法:

  • 使用object中的wait()方法让线程等待,使用object中的notify()方法唤醒线程
//这两种都需要上锁,并唤醒在等待后面才能唤醒线程
static final Object object = new Object();

private static void test1() {
    new Thread(() -> {
        try {
            //睡眠3秒钟,让其他线程先执行
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synchronized (object) {
            System.out.println(Thread.currentThread().getName() + " 线程启动");
            try {
                System.out.println(Thread.currentThread().getName() + " 线程等待");
                object.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " 线程被唤醒");
        }
    }, "test1--A").start();

    new Thread(() -> {
        synchronized (object) {
            //线程执行唤醒,在等待前面执行,后面的等待的线程不hi被唤醒
            object.notify();
            System.out.println(Thread.currentThread().getName() + " 线程启动 通知");
        }
    }, "test1--B").start();
}
  • 使用Juc包中Condition的await()方法让线程等待,使用signal()方法唤醒线程
//这两种都需要上锁,并唤醒在等待后面才能唤醒线程
static final Object object = new Object();
static final Lock lock = new ReentrantLock();
static final Condition condition = lock.newCondition();

private static void test2() {
    new Thread(() -> {
        try {
            //睡眠3秒钟,让其他线程先执行
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //上锁
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 线程启动");
            System.out.println(Thread.currentThread().getName() + " 线程等待");
            //线程等待
            condition.await();
            System.out.println(Thread.currentThread().getName() + " 线程被唤醒");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //释放锁
            lock.unlock();
        }
    }, "test2--A").start();

    new Thread(() -> {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " 线程启动 通知");
            condition.signal();
        } finally {
            lock.unlock();
        }
    }, "test2--B").start();
}
  • LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程
//使用java.util.concurrent.locks.LockSupport中的park和unpark 可不用加锁,可以先唤醒后等待 但是根本不得等待相当与直接赋予许可证
private static void test3() {
    Thread threadA = new Thread(() -> {
        try {
            //睡眠3秒钟,让其他线程先执行
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " 线程启动");
        System.out.println(Thread.currentThread().getName() + " 线程等待");
        //线程等待
        //他要给予许可证才能被唤醒
        //park() 方法相当于消费许可证,有许可证就通行,没有消费到就等待
        java.util.concurrent.locks.LockSupport.park();
        System.out.println(Thread.currentThread().getName() + " 线程被唤醒");
    }, "test3--A");
    threadA.start();

    new Thread(() -> {
        System.out.println(Thread.currentThread().getName() + " 线程启动 通知");
        //通过传入的线程,来赋予线程许可证
        //unpark() 方法相当于给线程发证,一个线程最多只能拥有一个许可证
        java.util.concurrent.locks.LockSupport.unpark(threadA);
    }, "test3--B").start();
}

你可能感兴趣的:(JUC)