iPhone 18 Pro变厚:告别“超薄”怪圈,开启手机“功能优先”新周期
2026/7/13 9:37:25
今天来聊聊Java多线程里一个超级经典的话题——等待唤醒机制。
在多线程编程里,我们经常遇到“线程协作”的场景:比如一个线程生产数据,另一个线程消费数据;或者两个线程需要严格交替执行(像乒乓球一样你一下我一下)。这时候就需要“等待唤醒”:条件不满足时线程自己睡一觉,等条件好了再被叫醒继续干活。
Java提供了两种实现方式:
synchronized + wait()/notify()Lock + Condition今天重点讲Lock + Condition,因为它更灵活、更高效,是现代并发编程的主流(Java并发包里的阻塞队列都是用这个实现的)。我们会用一个简单例子——两个线程交替打印0~9——来一步步讲解。
这是JDK 1.0就有的方式,简单但有局限。
核心规则:
synchronized同步块里调用wait()/notify()。wait():当前线程释放锁,进入等待状态(睡大觉)。notify():随机唤醒一个等待线程。notifyAll():唤醒所有等待线程(生产者消费者场景通常用这个,避免“假死”)。必须用while检查条件(重要!防止伪唤醒)。
简单例子(交替打印):
publicclassSyncWaitNotifyDemo{privatestaticfinal Object lock=newObject();// 共享锁对象privatestaticint num=0;privatestaticboolean isATurn=true;// true: A的回合publicstaticvoidmain(String[]args){newThread(()->{while(num<10){synchronized(lock){while(!isATurn){// 用while防伪唤醒try{lock.wait();// 不是我的回合,释放锁等待}catch(InterruptedException e){e.printStackTrace();}}System.out.println("A打印: "+num++);isATurn=false;lock.notifyAll();// 唤醒所有(安全)}}},"A").start();newThread(()->{while(num<10){synchronized(lock){while(isATurn){try{lock.wait();}catch(InterruptedException e){e.printStackTrace();}}System.out.println("B打印: "+num++);isATurn=true;lock.notifyAll();}}},"B").start();}}它能工作,但缺点明显:
从JDK 1.5开始,JUC包(java.util.concurrent)引入了ReentrantLock和Condition,彻底升级了等待唤醒机制。
完整例子:交替打印0~9(带详细注释)
Javaimport java.util.concurrent.locks.Condition;importjava.util.concurrent.locks.Lock;importjava.util.concurrent.locks.ReentrantLock;publicclassLockConditionDemo{privatestaticfinal Lock lock=newReentrantLock();// 一把可重入锁,所有线程共享privatestaticfinal Condition condA=lock.newCondition();// A线程专属等待队列(约定)privatestaticfinal Condition condB=lock.newCondition();// B线程专属等待队列(约定)privatestaticint num=0;// 当前数字privatestaticboolean isATurn=true;// true: 轮到A(初始让A先)publicstaticvoidmain(String[]args){// A线程:打印偶数newThread(()->{while(num<10){// 直到打印完9lock.lock();// 1. 先拿锁try{while(!isATurn){// 2. 用while检查:不是我的回合就等condA.await();// 释放锁,当前线程(A)进入condA队列睡觉}// 到这说明轮到我了,且重新拿到了锁System.out.println("A打印: "+num);num++;isATurn=false;// 交给BcondB.signal();// 精确唤醒B(只从condB队列拿一个)}catch(InterruptedException e){e.printStackTrace();}finally{lock.unlock();// 3. 一定释放锁!}}},"A").start();// B线程:打印奇数newThread(()->{while(num<10){lock.lock();try{while(isATurn){// 不是我的回合就等condB.await();// B线程进入condB队列}System.out.println("B打印: "+num);num++;isATurn=true;condA.signal();// 精确唤醒A}catch(InterruptedException e){e.printStackTrace();}finally{lock.unlock();}}},"B").start();}}运行结果(完美交替):
textA打印: 0
B打印: 1
A打印: 2
B打印: 3
…
B打印: 9
Java的ArrayBlockingQueue就是用一个Lock + 两个Condition实现的:
notEmpty:所有消费者共享的等待队列(队列空时await)。
notFull:所有生产者共享的等待队列(队列满时await)。
生产后notEmpty.signal()(只唤醒一个消费者)。
消费后notFull.signal()(只唤醒一个生产者)。
多生产者/多消费者时,它们共享同一个Condition队列,signal()只唤醒一个就够了——超级高效!