多线程交替打印ABC

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();
    }
}

   转载规则


《多线程交替打印ABC》 锦泉 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录