/**
*
* 线程等待与唤醒的三个方式:
* 1、Synchronized锁与 wait + notify
* 2、lock锁 与 lock.Condition await与signal
* 3、lockSupport park和unpark
*
*/
方式一,Synchronized锁与 wait + notify
public static void main(String[] args) { // 1、Synchronized锁与 wait + notify Object o = new Object(); new Thread(() -> { synchronized (o) { System.out.println(Thread.currentThread().getName() + "come in"); try { o.wait(); System.out.println(Thread.currentThread().getName() + "被唤醒"); } catch (InterruptedException e) { e.printStackTrace(); } } }, "thread 01").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ synchronized (o){ System.out.println(Thread.currentThread().getName() + "执行唤醒任务"); o.notify(); } },"thread 02").start(); }
1、去掉锁的情况
public static void main(String[] args) {
// 1、Synchronized锁与 wait + notify
Object o = new Object();
new Thread(() -> {
// synchronized (o) {
System.out.println(Thread.currentThread().getName() + "come in");
try {
o.wait();
System.out.println(Thread.currentThread().getName() + "被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
}
// }
}, "thread 01").start();
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(()->{
synchronized (o){
System.out.println(Thread.currentThread().getName() + "执行唤醒任务");
o.notify();
}
},"thread 02").start();
}
线程一调用wait()报出了不合法的监视器状态异常。
该方法是Object类的实例方法,该方法调用可能会抛出两个异常,一个是IllegalMonitorStateException,如果当前线程没有该对象的监视器锁时会抛出这个异常,由于线程一注释了synchronized锁,所以未持有对象的监视器锁。InterruptedException 如果当前线程处于等待通知状态期间被设置中断状态时抛出,同时清除中断标志。notify方法也需要获得监视器对象
2、交换唤醒与等待的顺序
public static void main(String[] args) {
// 1、Synchronized锁与 wait + notify
Object o = new Object();
new Thread(() -> {
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
synchronized (o) {
System.out.println(Thread.currentThread().getName() + "come in");
try {
o.wait();
System.out.println(Thread.currentThread().getName() + "被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "thread 01").start();
new Thread(()->{
synchronized (o){
System.out.println(Thread.currentThread().getName() + "执行唤醒任务");
o.notify();
}
},"thread 02").start();
}
线程一无法被唤醒。
方式一等待与唤醒的缺点:①需要锁对象 ②需要先等待后唤醒
方式二、lock锁 与 lock.Condition await与signore
public static void main(String[] args) {
// 2、lock锁 与 lock.Condition await与signore
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
new Thread(()->{
try{
lock.lock();
System.out.println(Thread.currentThread().getName() + " come in");
condition.await();
System.out.println(Thread.currentThread().getName() + "被唤醒");
}catch (Exception e){
}finally {
lock.unlock();
}
},"thread 01").start();
new Thread(()->{
try{
lock.lock();
System.out.println(Thread.currentThread().getName() + "执行唤醒");
condition.signal();
}catch (Exception e){
}finally {
lock.unlock();
}
},"thread 02").start();
}
情况一、是否可以不加锁
public static void main(String[] args) {
// 2、lock锁 与 lock.Condition await与signore
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
new Thread(()->{
try{
// lock.lock();
System.out.println(Thread.currentThread().getName() + " come in");
condition.await();
System.out.println(Thread.currentThread().getName() + "被唤醒");
}catch (Exception e){
e.printStackTrace();
}finally {
// lock.unlock();
}
},"thread 01").start();
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(()->{
try{
// lock.lock();
System.out.println(Thread.currentThread().getName() + "执行唤醒");
condition.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
// lock.unlock();
}
},"thread 02").start();
}
同样时需要锁对象
情况二、是否可以交换唤醒与等待的顺序
public static void main(String[] args) {
// 2、lock锁 与 lock.Condition await与signore
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
new Thread(()->{
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
try{
lock.lock();
System.out.println(Thread.currentThread().getName() + " come in");
condition.await();
System.out.println(Thread.currentThread().getName() + "被唤醒");
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
},"thread 01").start();
new Thread(()->{
try{
lock.lock();
System.out.println(Thread.currentThread().getName() + "执行唤醒");
condition.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
},"thread 02").start();
}
同样无法唤醒,相比方式一synchronized是一种隐式锁,lock锁可以手动上锁和释放,以及可以设置trylock抢锁时间返回抢锁的结果,锁更加灵活
方式三、locksupport park与unpark
public static void main(String[] args) {
// 3、lockSupport park和unpark
Thread thread = new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " come in");
LockSupport.park();
System.out.println(Thread.currentThread().getName() + "被唤醒");
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}, "thread 01");
thread.start();
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(()->{
try{
System.out.println(Thread.currentThread().getName() + "执行唤醒");
LockSupport.unpark(thread);
}catch (Exception e){
e.printStackTrace();
}finally {
}
},"thread 02").start();
}
LockSupport park方法需要一个凭证,如果当前线程没有凭证,就会处于休眠状态,直到unpark(线程对象)给线程发放凭证(最多只有一个凭证),或者被调用interrupt,或者被调用虚假返回
情况一、不需要我们去获取锁,他是通过阻塞原语进行线程阻塞
情况二、交换顺序
public static void main(String[] args) {
// 3、lockSupport park和unpark
Thread thread = new Thread(() -> {
try {
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName() + " come in");
LockSupport.park();
System.out.println(Thread.currentThread().getName() + "被唤醒");
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}, "thread 01");
thread.start();
new Thread(()->{
try{
System.out.println(Thread.currentThread().getName() + "执行唤醒");
LockSupport.unpark(thread);
}catch (Exception e){
e.printStackTrace();
}finally {
}
},"thread 02").start();
}
可以先唤醒在等待,也可以保证线程一的执行,先唤醒时,首先颁发了一个凭证,park时直接消费
这样在park消费时,可以直接忽略这个步骤
LockSupport相比前面的情况他可以无锁阻塞,以及支持先唤醒后阻塞 ,极大程度避免了线程耗死的情况