wait/notify等待通知机制入门学习

正在找实习的我真的是难过,没什么公司通知我去面试,唉...  闲着没事学习学习多线程方面知识。
PS:此笔记根据《java多线程编程核心技术》学习而来,新手,有错误的地方希望谅解然后多多指出!!!

1.什么是等待通知机制呢?

举个例子,我们去饭店首先要点菜,然后服务员需要将点的菜告诉厨师,厨师才能对应着菜单进行做饭,这时候呢服务员不可能一直问厨师这个菜做好没那个菜做好没,这样效率会非常低下。于是服务员在这个时候可以去干其他事情,比如调戏下男服务员一起去拉个翔啥的…

当厨师做好菜时,才会通知服务员,这桌的菜做好啦,你可以上给顾客了,于是服务员就将菜呈上给我们,这就是等待通知机制。

2.等待/通知机制的实现

方法wait()的作用是使当前执行代码的线程进行等待。在调用wait()之前,线程必须获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait方法。如果调用wait时没有持有适当的锁,就会抛出IllegalMonitorStateException,它是RuntimeException的一个子类,因此不需要try-catch进行捕捉。

方法notify()作用是用来通知等待该对象的对象锁的其他线程,如果有多个线程等待,则有线程规划器随机挑选一个在wait的线程。同样需要在同步方法和同步块中调用,在调用之前,也要获得该对象的对象级别锁。如果调用notify时没持有适当的锁,也会抛出IllegalMonitorStateException。需要注意的是,执行notif方法的线程要将程序执行完,也就是推出synchronized代码块后,当前线程才会释放锁,wait状态的线程才能获得该对象锁。

总之一句话:wait使线程停止,notify使停止的线程继续运行。

光说不做可不行,实战练习

创建test2项目

package com.zz.test2;



import java.util.ArrayList;



public class MyList {

private static ArrayList list = new ArrayList();

public static void add() {

list.add("Anything");

}

public static int size() {

return list.size();

}

}

创建Thread1类

public class Thread1 extends Thread{

private Object lock;

public Thread1(Object lock) {

super();

this.lock = lock;

}

@Override

public void run() {

try {

synchronized (lock) {

if(MyList.size() != 5) {

System.out.println("wait begin" + System.currentTimeMillis());

lock.wait(); //list长度不等于5就进行等待

System.out.println("wait end" + System.currentTimeMillis());

}

}

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

创建Thread2类

public class Thread2 extends Thread{

private Object lock;

public Thread2(Object lock) {

super();

this.lock = lock;

}

@Override

public void run() {

try {

synchronized (lock) {

for(int i = 0; i < 10; i++) {

MyList.add();

if(MyList.size() == 5) {

lock.notify(); //list长度等于5就会发出通知,但同步块依旧运行,只有运行完毕才会释放对象锁

System.out.println("已发送通知");

}

System.out.println("加添了" + (i + 1) + "个元素");

Thread.sleep(1000);

}

}

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

最后Run一个

public class Run {

public static void main(String[] args) throws InterruptedException {

Object lock = new Object();

Thread1 a = new Thread1(lock);

a.start();

Thread.sleep(50);

Thread2 b = new Thread2(lock);

b.start();

}

}

运行结果:

wait begin1553767130564

加添了1个元素

加添了2个元素

加添了3个元素

加添了4个元素

已发送通知

加添了5个元素

加添了6个元素

加添了7个元素

加添了8个元素

加添了9个元素

加添了10个元素

wait end1553767140663

wait end在最后输出,也说明notify()方法在执行之后并不立即释放锁

wait方法可以使方法的线程释放当前共享资源的锁,从运行状态退出进入等待队列,直到被再次唤醒。
notify方法可以随机唤醒一个在等待队列的同一个共享资源的 “ 一个 ” 等待线程。
notifyAll方法可以唤醒在等待队列的同一个共享资源的 “ 全部 ” 等待线程。此时优先级最高的线程先执行,也有可能是随机一个,这要看JVM虚拟机的实现。

等待wait的条件发生变化

在使用wait/notify模式时,需要注意一个事项,那就是等待的条件如果发生混乱,容易造成程序逻辑的混乱,所以咱们创建个项目来看是怎么个混乱法

Add类:

package com.zz.waitOld;



public class Add {

private String lock;

public Add(String lock) {

super();

this.lock = lock;

}

public void add() {

synchronized (lock) {

ValueObjcet.list.add("Anything");

lock.notifyAll();

}

}

}

Subtract类:

package com.zz.waitOld;



public class Subtract {

private String lock;



public Subtract(String lock) {

super();

this.lock = lock;

}


public void subtract() {

try {

synchronized (lock) {

if (ValueObjcet.list.size() == 0) {

System.out.println(" wait begin Name = " + Thread.currentThread().getName());

lock.wait();

System.out.println(" wait end Name = " + Thread.currentThread().getName());

}

ValueObjcet.list.remove(0);

System.out.println("list size=" + ValueObjcet.list.size());

}

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

ThreadAdd类:

package com.zz.waitOld;



public class ThreadAdd extends Thread {

private Add p;

public ThreadAdd(Add p) {

super();

this.p = p;

}

public void run() {

p.add();

}

}

ThreadSubject类:

package com.zz.waitOld;



public class ThreadSubject extends Thread {

private Subtract p;

public ThreadSubject(Subtract p) {

super();

this.p = p;

}

public void run() {

p.subtract();

}

}

LIst类:

package com.zz.waitOld;



import java.util.ArrayList;



public class ValueObjcet {

public static ArrayList list = new ArrayList();

}

最后一个当然是Run类啦:

package com.zz.waitOld;

public class Run {

public static void main(String[] args) throws InterruptedException {

String lock = new String("");

Add add = new Add(lock);

Subtract subtract = new Subtract(lock);

ThreadSubject ts1 = new ThreadSubject(subtract);

ts1.setName("subtract1Thread");

ts1.start();

ThreadSubject ts2 = new ThreadSubject(subtract);

ts2.setName("subtract2Thread");

ts2.start();

Thread.sleep(1000);

ThreadAdd addThread = new ThreadAdd(add);

addThread.setName("addThread");

addThread.start();

}

}

运行结果:

wait begin Name = subtract1Thread

wait begin Name = subtract2Thread

wait end Name = subtract2Thread

Exception in thread "subtract1Thread" list size=0

wait end Name = subtract1Thread

java.lang.IndexOutOfBoundsException: Index: 0, Size: 0

at java.util.ArrayList.rangeCheck(Unknown Source)

at java.util.ArrayList.remove(Unknown Source)

at com.zz.waitOld.Subtract.subtract(Subtract.java:19)

at com.zz.waitOld.ThreadSubject.run(ThreadSubject.java:13)

这是由于list经过一次删除后,已经无数据,但是先第二次remover()的线程操作后,造成了索引溢出的异常,所以只需改动一个地方Subtract类,使程序逻辑改变一下即可

Subtract类:

package com.zz.waitOld;



public class Subtract {

private String lock;



public Subtract(String lock) {

super();

this.lock = lock;

}



public void subtract() {

try {

synchronized (lock) {

while (ValueObjcet.list.size() == 0) { //仅此一个地方改动

System.out.println(" wait begin Name = " + Thread.currentThread().getName());

lock.wait();

System.out.println(" wait end Name = " + Thread.currentThread().getName());

}

ValueObjcet.list.remove(0);

System.out.println("list size=" + ValueObjcet.list.size());

}

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

运行结果我就不粘贴了,手动敲代码运行起来才是最爽的!!!
话说为什么我觉得这个编辑器怎么这么难用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值