面试之多线程交替输出

简述

  面试中常常被问到多线程交替输出数字或字母序列的问题,例如AB两个线程交替输出1到100。三个线程交替输出abcabcabc...等

wait notify实现3线程交替输出abc

  我们首先抽象出一个用于打印字母的类:

package ThreadTest.testOut;

public class SyncWaitNotify {
    int flag;
    int loopNumber;

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

    public void print(int waitFlag,int nextFlag,String str){
        synchronized (this){
            while(waitFlag != nextFlag){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(str);
            flag = nextFlag;
            this.notifyAll();
        }
    }

}

  需要注意我们这里用整形flag的123代表abc,当前的flag不等于等待的flag时,将会进入等待队列等待。

  当被唤醒后则则会更新标记并唤醒其余线程进行输出,线程运行代码:

package ThreadTest.testOut;

public class TestABC {
    public static void main(String[] args) {
        SyncWaitNotify syncWaitNotify = new SyncWaitNotify(1, 5);
        new Thread(() -> {
            syncWaitNotify.print(1, 2, "a");
        }).start();
        new Thread(() -> {
            syncWaitNotify.print(2, 3, "b");
        }).start();
        new Thread(() -> {
            syncWaitNotify.print(3, 1, "c");
        }).start();
    }
}

 await singal实现交替输出abc

  这个和wait notify不同之处在于,wait notify一唤醒就唤醒全部,醒来了再去判断自己该不该执行任务,不该的话就继续wait。而await singal有三个condiction,每个线程完成任务后可以知道该唤醒哪一个线程

package ThreadTest.testOut;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class TestABC2 {
    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(1000);
        awaitSignal.lock();
        try{
            a.signal();
        }
        finally {
            awaitSignal.unlock();
        }
    }
}

class AwaitSignal extends ReentrantLock{
    private int loopNumber;
    public AwaitSignal(int loopNumber){
        this.loopNumber = loopNumber;
    }
    //参数1:打印内容     参数2:进入哪间休息室    参数3:下一间休息室
    public void print(String str,Condition current,Condition next){
        for(int i=0;i<loopNumber;i++){
            lock();
            try{
                current.await();
                System.out.print(str);
                next.signal();//唤醒下一间休息室
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                unlock();
            }
        }
    }
}

park和unpark实现交替输出abc

  我们还可以使用park unpark完成此任务,这种实现方式是以线程为单位的,所以代码更加简洁

package ThreadTest.testOut;

import java.util.concurrent.locks.LockSupport;

public class TestABC3 {

    static Thread t1;
    static Thread t2;
    static Thread t3;

    public static void main(String[] args) {
        ParkUnpark parkUnpark = new ParkUnpark(5);
        t1 = new Thread(()->{
            parkUnpark.print("a",t2);
        });
        t2 = new Thread(()->{
            parkUnpark.print("b",t3);
        });
        t3 = new Thread(()->{
            parkUnpark.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);
        }
    }
}

 

 

 

 

 

  

posted @ 2021-08-10 08:08  艾尔夏尔-Layton  阅读(143)  评论(0编辑  收藏  举报