AQS
LockSupport
LockSupport是什么
java.util.concurrent.locks下的一个类 LockSuport
LockSupport是用来创建锁和其他同步类的基本线程阻塞原语
LockSupport中的park()和unpark()的作用分别是阻塞线程和解除阻塞线程
线程等待唤醒机制(wait/notify)
三种让线程等待和唤醒的方法
方式一:使用Object中的wait()方法让线程等待,使用Object中的notify()方法唤醒线程
方式二:使用JUC包中的Condition的await()方法让线程等待,使用signal()方法唤醒线程
方式三:LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程
Object类中的wait和notify方法实现线程等待和唤醒
代码
正常
public class WaitNotifyDemo {
static Object objectLock = new Object();
public static void main(String[] args) {
new Thread(()->{
synchronized (objectLock){
System.out.println("come in");
try {
objectLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("解锁");
}
},"AAA").start();
new Thread(()->{
synchronized (objectLock){
System.out.println("----");
objectLock.notify();
}
},"BBB").start();
}
}
异常1
wait 和notify方法必须要在同步代码块或者方法里面且成对出现使用
否则,
Exception in thread "AAA" Exception in thread "BBB" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at com.zhj.test.WaitNotifyDemo.lambda$main$1(WaitNotifyDemo.java:22)
at java.lang.Thread.run(Thread.java:750)
java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at com.zhj.test.WaitNotifyDemo.lambda$main$0(WaitNotifyDemo.java:11)
at java.lang.Thread.run(Thread.java:750)
异常2
将notify放在wait方法之前
程序无法执行,无法唤醒
总结
- wait和notify方法必须要要在同步代码块或者方法里面且成对出现使用
- 先wait后notify才ok
Condition接口中的await和signal方法实现线程的等待和唤醒
demo
public class ConditionWaitDemo {
static ReentrantLock lock = new ReentrantLock();
static Condition condition = lock.newCondition();
public static void main(String[] args) {
new Thread(()->{
lock.lock();
try {
System.out.println("come in ");
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("解锁");
lock.unlock();
}
},"AAA").start();
new Thread(()->{
lock.lock();
try {
System.out.println("---");
condition.signal();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
},"BBB").start();
}
}
异常 与 synchronized 相同
传统的synchronized和lock实现等待唤醒通知的约束
- 线程先要获得并持有锁,必须在锁块(synchronized或lock)中
- 必须要 先等待后唤醒,线程才能够被唤醒
LockSupport类中的park等待和唤醒
是什么
LockSupport是用来创建锁和其他同步类的基本线程阻塞原语
LockSupport类使用了一种名为permit(许可)
的概念做到阻塞和唤醒线程的功能,每个线程都有一个许可(permit),permit只有两个值1和0,默认是0
可以把许可看成(0,1)信号量(Semaphore),但与Semaphore不同的是,许可的累加上限是1
方法
- 阻塞
park/park(Object blocker)
阻塞当前线程/阻塞传入的具体线程
- 唤醒
unpark(Thread thread)
唤醒处于阻塞状态的线程
demo
public class LockSupportDemo {
public static void main(String[] args) {
Thread a = new Thread(() -> {
System.out.println("come in");
LockSupport.park();
System.out.println("解锁");
}, "AAA");
a.start();
Thread bbb = new Thread(() -> {
System.out.println("----");
LockSupport.unpark(a);
}, "BBB");
bbb.start();
}
}
解决问题:
不受锁块的影响
可以先唤醒后等待
重要说明
LockSupport是用来创建锁和其他同步类的基本线程阻塞原语
LockSupport是一个线程阻塞工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞,阻塞之后也有对应的唤醒方法。归根结底,LockSupport调用的Unsafe中的native代码
LockSupport提供的park()和unpark()方法实现阻塞线程和解除线程阻塞的过程
LockSupport和每个使用他的线程都有一个许可关联。permit 相当于1或0的开关,默认是0
形象的理解
线程阻塞需要消耗凭证(permit),这个凭证最多只有一个
当调用park方法时
- 如果有凭证,则会直接消耗这个凭证然后正常退出
- 如果无凭证,就会阻塞等待凭证可用
连续两次调用park会阻塞
而unpark则相反
他会增加一个凭证,但凭证最多只能有1个,累加无效