- 线程协作
所谓线程协作也就是多线程并发编程中,线程与线程之间完成同一个业务逻辑,期间可能有线程等待、线程通知。
- 线程等待实现方式
Thread线程类的sleep方法,等待指定时间后自动唤醒线程。Object类的wait 方法,线程睡眠后只能通过Object的notify或notifyAll唤醒线程。
notify 方法随机唤醒多个等待线程中的一个,而notifyAll方法则唤醒所有等待线程。
- wait、notify或notifyAll方法使用注意项
- wait和notify使用的模范代码逻辑如下
等待通知的代码逻辑一般为: synchronized(对象){ while(条件不满足){ 对象.wait(); } //业务逻辑 } 接收通知的代码逻辑一般为: synchronized(对象){ //业务逻辑,改变条件 对象.notify/notifyAll(); }
-
wait和notify方法使用必须添加synchronized锁,否则报异常 java.lang.IllegalMonitorStateException
-
锁对象和wait、notify调用的对象必须是同一个对象
-
一个方法中可以同时出现wait和notify或notifyAll,没有任何影响,只是取决于业务逻辑
注意:wait和notify方法使用都要加锁,线程调用wait方法后,另一个线程怎么还会获取到锁?
wait方法内部原理,wait方法调用时其实是把当前线程在wait代码行处进行休眠,同时释放锁。
- wait和notifyAll方法案例,实现消费者和生产者模式
一个线程创建橘子到队列中,队列满了通知监视官,同时休眠。另一个线程判断队列中有橘子则进行消费,没有则通知监视官,同时休眠。监视官接到通知后,分别派遣创建线程或消费线程进行创建或消费,从而让系统保持平衡进行。
创建和消费代码如下:
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.LinkedBlockingQueue;
/**
* 生产橘子,消费橘子
*/
public class CreConsumerOrange {
//工厂监视员(notify/wait 锁对象).不管是生产者,还是消费者,做完本职工作都要通知一下监视官
private Object monitorLock;
//创建橘子随机大小
private static final Random random=new Random();
private static final int queueSize=5;
private static final Queue<Integer> queue=new LinkedBlockingQueue<>();
public CreConsumerOrange(Object monitor){
this.monitorLock=monitor;
}
//生产线程-生产,创建橘子,队列小于5则创建
public void createOrange(){
while(true) {
synchronized (monitorLock){
//生产
if(queue.size()<queueSize){
while (queue.size()<queueSize) {
int orange = random.nextInt(20);
queue.add(orange);
System.out.println(Thread.currentThread().getName()+"===========create orange "+orange);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//通知monitorLock ,橘子已经创建完成
monitorLock.notifyAll();
}
else{
try {
monitorLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
//消费线程-消费
public void consumeOrange(){
while(true) {
synchronized (monitorLock) {
if(queue.size()>0) {
while (queue.size() >= queueSize) {
Integer peek = queue.remove();
System.out.println(Thread.currentThread().getName()+"=======consumeOrage size is " + peek);
}
monitorLock.notifyAll();
}else{
try {
monitorLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//模拟消费者话费时间
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
创建线程进行消费代码如下:1个线程创建,同时2个线程进行消费
public class NotifyTest {
public static void main(String[] args) {
Object monitor =new Object();//监视官
CreConsumerOrange factory=new CreConsumerOrange(monitor);
//消费者
new Thread(new Runnable() {
@Override
public void run() {
factory.consumeOrange();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
factory.consumeOrange();
}
}).start();
//生产者
new Thread(new Runnable() {
@Override
public void run() {
factory.createOrange();
}
}).start();
System.out.println("===============main is end===================");
}
}
执行结果如下:
===============main is end===================
Thread-2===========create orange 7
Thread-2===========create orange 0
Thread-2===========create orange 11
Thread-2===========create orange 3
Thread-2===========create orange 12
Thread-1=======consumeOrage size is 7
Thread-2===========create orange 14
Thread-0=======consumeOrage size is 0
Thread-2===========create orange 0
Thread-1=======consumeOrage size is 11
Thread-2===========create orange 16
Thread-0=======consumeOrage size is 3
Thread-2===========create orange 19
...
...