1、可重入锁(递归锁)
是指在同一线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提,锁对象得是同一对象),不会因为之前已经获取过没有释放而阻塞。
java中的ReentrantLock和synchronized都是可重入锁,可重入锁的有点就是可以避免死锁。
可重入锁种类
隐式锁(即synchronized关键字使用的锁)默认是可重入锁。
monitorenter monitorexit monitorexit出现两次是为了保证出现异常时彻底释放锁
可重入锁的实现机制
每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针。
当执行monitorenter时,如果目标锁对象的计数器为零,那么说明它没有被其他线程所持有,java虚拟机会将该锁对象的持有线程设置为当前线程,并将计数器+1。
在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程是当前线程,那么java线程将计数器+1,否则需要等待,知道持有线程释放锁。
当执行monitorexit时,java虚拟机则需要将锁对象的计数器减1.计数器为零代表锁已经被释放。
显示锁(即Lock)也有ReentrantLock这样的可重入锁。
2、LockSupport(用于创建锁和其他同步类的基本线程阻塞原语)
线程等待唤醒机制(wait/notify)加强版
三种线程等待唤醒的方法
方式一:使用object类中的wait()方法让线程等待,使用object类中的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() + "\t" + "====come in");
try {
objectLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "\t" + "====被唤醒");
}
}, "A").start();
new Thread(() -> {
synchronized (objectLock) {
objectLock.notify();
System.out.println(Thread.currentThread().getName() + "\t" + "====通知");
}
}, "B").start();
}
}
问题:wait和notify只能在synchronized代码块中执行,并且notify只能在wait之后若顺序颠倒会出现阻塞现象
方式二:使用JUC包中的Condition的await()方法让线程等待,使用signal()方法唤醒线程
public class LockSupportDemo {
static Lock lock = new ReentrantLock();
static Condition condition = lock.newCondition();
public static void main(String[] args) {
new Thread(() -> {
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+ "\t"+ "====come in");
condition.await();
System.out.println(Thread.currentThread().getName()+ "\t"+ "====被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
},"A").start();
new Thread(() -> {
lock.lock();
try {
condition.signal();
System.out.println(Thread.currentThread().getName()+ "\t"+ "====通知");
} finally {
lock.unlock();
}
},"B").start();
}
}
问题:必须在锁块lock中,且需要先await再signal
方式三:LockSupport类可以阻塞当前线程以及唤醒执行被阻塞的线程
public class LockSupportDemo {
public static void main(String[] args) throws InterruptedException {
Thread a = new Thread(()->{
System.out.println(Thread.currentThread().getName()+"\t"+"====come in");
LockSupport.park();
System.out.println(Thread.currentThread().getName()+"\t"+"====被唤醒");
},"a");
a.start();
Thread.sleep(3000);
Thread b = new Thread(()->{
LockSupport.unpark(a);
System.out.println(Thread.currentThread().getName()+"\t"+"====通知");
},"b");
b.start();
}
}
优点,unpark()可以在park()之前执行,不需要再任何操作之后