wait和notify
wait( ),notify( ),notifyAll( )
都不属于Thread类,而是属于Object基础类,也就是每个对象都有wait( ),notify( ),notifyAll( )
的功能,因为每个对象都有锁,锁是每个对象的基础,当然操作锁的方法也是最基础了。- 当需要调用以上的方法的时候,一定要对竞争资源进行加锁,如果不加锁的话,则会报 IllegalMonitorStateException 异常
- 当想要调用
wait( )
进行线程等待时,必须要取得这个锁对象的控制权(对象监视器),一般是放到synchronized(obj)
代码中。 - 在while循环里而不是if语句下使用wait,这样,会在线程暂停恢复后都检查wait的条件,并在条件实际上并未改变的情况下处理唤醒通知
- 调用
obj.wait( )
释放了obj的锁,否则其他线程也无法获得obj的锁,也就无法在synchronized(obj){ obj.notify() }
代码段内唤醒A。 notify( )
方法只会通知等待队列中的第一个相关线程(不会通知优先级比较高的线程)notifyAll( )
通知所有等待该竞争资源的线程(也不会按照线程的优先级来执行)- 假设有三个线程执行了
obj.wait( )
,那么obj.notifyAll( )
则能全部唤醒tread1,thread2,thread3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁,因此,tread1,thread2,thread3只有一个有机会获得锁继续执行,例如tread1,其余的需要等待thread1释放obj锁之后才能继续执行。 - 当调用obj.notify/notifyAll后,调用线程依旧持有obj锁,因此,thread1,thread2,thread3虽被唤醒,但是仍无法获得obj锁。直到调用线程退出synchronized块,释放obj锁后,thread1,thread2,thread3中的一个才有机会获得锁继续执行。
交替打印ABC
public class PrintABC implements Runnable{
private String name;
private final Object pre;
private final Object next;
public PrintABC(String name,Object pre,Object next) {
this.name = name;
this.pre = pre;
this.next = next;
}
@Override
public void run(){
int count = 10;
while(count > 0){
synchronized (pre){
synchronized (next){
count--;
System.out.println(name);
next.notify();
}
try {
pre.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Object a = new Object();
Object b = new Object();
Object c = new Object();
PrintABC pa = new PrintABC("A",c,a);
PrintABC pb = new PrintABC("B",a,b);
PrintABC pc = new PrintABC("C",b,c);
new Thread(pa).start();
Thread.sleep(100);
new Thread(pb).start();
Thread.sleep(100);
new Thread(pc).start();
}
}
obj.wait()
之后的代码不会执行,直到其他线程t2调用了obj.notify()
;
在其他线程t2调用了obj.notify()
线程t1不会立刻开始执行,需要等待线程t2的synchronized代码块执行完毕
交替打印AB
//这个变通好一点,上边那个不知道怎么改
public class PrintAB{
private int num;
private static final Object LOCK = new Object();
private void printAB(int targetNum) throws InterruptedException {
int cnt = 3;
while (cnt > 0){
synchronized(LOCK){
while(num % 2 != targetNum){
LOCK.wait();
}
System.out.println(Thread.currentThread().getName());
num++;
cnt--;
LOCK.notify();
}
}
}
public static void main(String[] args) {
PrintAB printAB = new PrintAB();
new Thread(()->{
try {
printAB.printAB(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(()->{
try {
printAB.printAB(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
},"B").start();
}
}
使用ReentrantLock
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class PrintABC_Lock {
// 当前状态值:保证三个线程之间交替打印
private int num;
private Lock lock = new ReentrantLock();
private void printABC(int aim){
for (int i = 0; i < 10; ){
lock.lock();
if (num % 3 == aim){
i++;
num++;
System.out.println(Thread.currentThread().getName());
}
lock.unlock();
}
}
public static void main(String[] args) {
PrintABC_Lock printABC_lock = new PrintABC_Lock();
new Thread(()->{printABC_lock.printABC(0);},"A").start();
new Thread(()->{printABC_lock.printABC(1);},"B").start();
new Thread(()->{printABC_lock.printABC(2);},"C").start();
}
}
使用condition
可以使用Lock+Condition实现对线程的精准唤醒,减少对同步锁的无意义竞争,浪费资源。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class PrintABC_Lock {
private int num = 0;
private ReentrantLock lock = new ReentrantLock();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
Condition conditionC = lock.newCondition();
private void printABC(int aim,Condition curThread, Condition nextThread){
for (int i = 0; i < 3; ){
lock.lock();
try{
while (num % 3 != aim){
//阻塞当前线程
curThread.await();
}
System.out.println(Thread.currentThread().getName());
num++;
i++;
//唤醒下一个线程,而不是唤醒所有线程
nextThread.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
PrintABC_Lock printABC_lock = new PrintABC_Lock();
new Thread(()->{printABC_lock.printABC(0,printABC_lock.conditionA,printABC_lock.conditionB);},"A").start();
new Thread(()->{printABC_lock.printABC(1,printABC_lock.conditionB,printABC_lock.conditionC);},"B").start();
new Thread(()->{printABC_lock.printABC(2,printABC_lock.conditionC,printABC_lock.conditionA);},"C").start();
}
}