经典面试题--同步模式之顺序控制

59 阅读2分钟

一、固定运行顺序

必须先 2 后 1 打印

1.1 wait notify 版

// 用来同步的对象
static Object obj = new Object();
// t2 运行标记, 代表 t2 是否执行过
static boolean t2runed = false;

public static void main(String[] args) {

    Thread t1 = new Thread(() -> {
        synchronized (obj) {
            // 如果 t2 没有执行过
            while (!t2runed) {
                try {
                    // t1 先等一会
                    obj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println(1);
    });

    Thread t2 = new Thread(() -> {
        System.out.println(2);
        synchronized (obj) {
            // 修改运行标记
            t2runed = true;
            // 通知 obj 上等待的线程(可能有多个,因此需要用 notifyAll)
            obj.notifyAll();
        }
    });

    t1.start();
    t2.start();
}

1.2 Park Unpark 版

Thread t1 = new Thread(() -> {
    LockSupport.park();
    System.out.println(1);
});

Thread t2 = new Thread(() -> {
    System.out.println(2);
    LockSupport.unpark(t1);
});

t1.start();
t2.start();

二、交替输出

线程 1 输出 a 5 次,线程 2 输出 b 5 次,线程 3 输出 c 5 次。现在要求输出 abcabcabcabcabc 怎么实现

2.1 wait notify 版

@Slf4j
public class test {
    public static void main(String[] args) {
        WaitNotify waitNotify = new WaitNotify(1, 5); //初始让1线程先执行,共循环5次

        new Thread(() -> waitNotify.print(1, 2, "a")).start();
        new Thread(() -> waitNotify.print(2, 3, "b")).start();
        new Thread(() -> waitNotify.print(3, 1, "c")).start();
    }
}

class WaitNotify {

    private int flag; //当前允许哪个线程执行

    private int loopNumber; //循环次数

    public WaitNotify(int flag, int loopNumber) {
        this.flag = flag;
        this.loopNumber = loopNumber;
    }

    public void print(int waitFlag, int nextFlag, String str) { //当前等待线程,下一个线程,打印字符
        for(int i = 0; i < loopNumber; i ++) {
           synchronized (this) {
               while(this.flag != waitFlag) {
                   try {
                       this.wait();
                   } catch (InterruptedException e) {
                       throw new RuntimeException(e);
                   }
               }
               this.flag = nextFlag;
               System.out.print(str + " ");
               this.notifyAll();
           }
        }
    }
}

2.2 Lock 条件变量版

@Slf4j
public class test {
    public static void main(String[] args) throws InterruptedException {
        AwaitSignal awaitSignal = new AwaitSignal(5);
        Condition a = awaitSignal.newCondition();
        Condition b = awaitSignal.newCondition();
        Condition c = awaitSignal.newCondition();
        new Thread(() -> awaitSignal.print("a", a, b)).start();
        new Thread(() -> awaitSignal.print("b", b, c)).start();
        new Thread(() -> awaitSignal.print("c", c, a)).start();

        Thread.sleep(1000L);

        awaitSignal.lock();
        try {
            System.out.println("开始");
            a.signal(); //唤醒和进入休息室的前提是获取锁 (被唤醒后,重新竞争锁,获取到后又可执行唤醒next线程操作)
        } finally {
            awaitSignal.unlock();
        }
    }
}

class AwaitSignal extends ReentrantLock {

    private int loopNumber;

    public AwaitSignal(int loopNumber) {
        this.loopNumber = loopNumber;
    }


    public void print(String str, Condition current, Condition next) {
        for(int i = 0; i < loopNumber; i ++) {
            lock();
            try {
                current.await(); //当前线程进入current休息室等待
                System.out.print(str + " ");
                next.signal(); //唤醒下一个线程
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                unlock();
            }
        }
    }

}

2.3 Park Unpark 版

@Slf4j
public class test {
    static Thread t1, t2, t3;
    public static void main(String[] args) throws InterruptedException {
        ParkUnpark pk = new ParkUnpark(5);

        t1 = new Thread(() -> pk.print("a", t2));
        t2 = new Thread(() -> pk.print("b", t3));
        t3 = new Thread(() -> pk.print("c", t1));

        t1.start();
        t2.start();
        t3.start();

        LockSupport.unpark(t1);
    }
}


class ParkUnpark {

    private int loopNumber;

    public ParkUnpark(int loopNumber) {
        this.loopNumber = loopNumber;
    }

    public void print(String str, Thread next) {
       for(int i = 0; i < loopNumber; i ++) {
           LockSupport.park();
           System.out.print(str + " ");
           LockSupport.unpark(next);
       }
    }

}