经典消费者,生产者案例(不同于操作系统的生消案例)
public class Producer implements Runnable {
private Queue<String> bags;
private int maxSize;
public Producer(Queue<String> bags, int maxSize) {
this.bags = bags;
this.maxSize = maxSize;
}
@Override
public void run() {
int i = 0;
while (true) {
i++;
synchronized (bags) { //抢占锁
if (bags.size() == maxSize) {
System.out.println("bags 满了");
try {
bags.wait();
//满了,阻塞当前线程加入到等待队列中并且释放锁,不等于同步代码块的结束
//wait底层实现是park(); ->JVM ->Native
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("生产者生产:bag" + i);
bags.add("bag" + i); //生产bag
bags.notify(); //表示当前已经生产了数据,提示消费者可以消费了
//把等待队列的一个线程移动到同步队列中,开始尝试抢占锁
}//同步代码快执行结束
}
}
}
public class Consumer implements Runnable{
private Queue<String> bags;
private int maxSize;
public Consumer(Queue<String> bags, int maxSize) {
this.bags = bags;
this.maxSize = maxSize;
}
@Override public void run() {
while(true){
synchronized (bags){
if(bags.isEmpty()){
System.out.println("bags为空");
try {
bags.wait();//同理
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String bag=bags.remove();
System.out.println("消费者消费:"+bag);
bags.notify(); //这里只是唤醒等待队列中的线程,移动至同步队列中
//但是Producer线程并不能马上执行,因为同步代码块还没执行完,monitorexit指令也未执行完成
} //同步代码块执行结束, monitorexit指令执行完成,Producer才有可能运行
}
}
}
Wait和Notify的流程
注意
- wait是把当前线程释放锁并加入到等待队列中
- notify是把等待队列的一个等待线程移动到同步队列中。
- 同步队列是一直在抢占锁
Join
当前线程调用,则其它线程全部停止,等待当前线程执行完毕,接着执行。
不难想到也是利用了通信机制,当前线程执行完发出一个信号,其它线程再执行。
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
没看到notify?这个notify是在JVM层面实现的
static void ensure_join(JavaThread* thread) {
Handle threadObj(thread, thread->threadObj());
assert(threadObj.not_null(), "java thread object must exist");
ObjectLocker lock(threadObj, thread);
thread->clear_pending_exception();
java_lang_Thread::set_thread_status(threadObj(),
java_lang_Thread::TERMINATED);
java_lang_Thread::set_thread(threadObj(), NULL);
lock.notify_all(thread);
thread->clear_pending_exception(); }
线程执行完会调用一个JVM的清理(exit)方法,然后会调用ensure_join方法,这个方法倒数第二行即notifyall。
Condition案例
Condition等价于Wait和Notify(是J.U.C包中实现的,跟Lock配套)
- 在J.U.C层面中,锁的实现是Lock
- wiat/notify,锁的实现是synchronized
public class ConditionDemoWait implements Runnable {
private Lock lock;
private Condition condition;
public ConditionDemoWait(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
@Override public void run() {
System.out.println("begin - ConditionDemoWait");
lock.lock();
try{
condition.await(); //让当前线程阻塞,Object.wait();
System.out.println("end - ConditionDemoWait");
} catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public class ConditionDemeNotify implements Runnable{
private Lock lock;
private Condition condition;
public ConditionDemeNotify(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
System.out.println("begin - ConditionDemeNotify");
lock.lock(); //synchronized(lock)
try{
condition.signal(); //让当前线程唤醒 Object.notify(); //因为任何对象都会 有monitor
//继续执行,因为signal只是唤醒,还抢不到锁,因为此线程还未释放锁
System.out.println("end - ConditionDemeNotify");
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public class ConditionExample {
public static void main(String[] args) {
Lock lock=new ReentrantLock();
Condition condition=lock.newCondition();
ConditionDemoWait cd=new ConditionDemoWait(lock,condition);
ConditionDemeNotify conditionDemeNotify=new ConditionDemeNotify(lock,condition);
new Thread(cd).start();
new Thread(conditionDemeNotify).start();
}
}
要点
- signal不会释放锁,await会释放锁
- 原理和wait/notify一样
Condition设计猜想
- 作用: 实现线程的阻塞和唤醒
- 前提条件: 必须先要获得锁
- await/signal/signalAll
- await 让线程阻塞, 并且释放锁
- signal -> 唤醒阻塞的线程
- await/signal/signalAll
- 加锁的操作,必然会涉及到AQS的阻塞队列(就是以前说的AQS的队列),未成功就放入阻塞队列。
- await 释放锁的时候, AQS队列中不存在已经释放锁的线程,这个被释放的线程一定存放在了conditon的等待队列,类似于wait的等待队列,也称条件队列
- signal 唤醒被阻塞的线程 从条件队列中,再次放入AQS队列。(AQS阻塞队列相当于wait/notify的同步队列)