美文网首页Java
一道多线程协同面试题的三种解决方案

一道多线程协同面试题的三种解决方案

作者: 冬天里的懒喵 | 来源:发表于2020-09-13 19:34 被阅读0次

最近看到一道线程协同的阿里面试题。

(JAVA)有3 个独立的线程,一个只会输出A,一个只会输出L,一个只会输出I。
在三个线程同时启动的情况下,请用合理的方式让他们按顺序打印ALIALI。
三个线程开始正常输出后,主线程若检测到用户任意的输入则停止三个打印线程的工作,整体退出。

这个题目实际上是在考察线程间协调。鉴于前面学习的线程间通信的三种方法,现在用三种方法来完成该问题。

1.synchronized 配合 wait/notify

首先用最经典的synchronized来实现。

import java.util.Scanner;
import java.util.concurrent.TimeUnit;

public class ThreeThreadTest {

    private final static Object lock = new Object();

    private static volatile int count = 0;

    public static void main(String[] args) {

        Thread t1 = new Thread(() -> {
            try {
                while (true) {
                    synchronized (lock) {
                        lock.notifyAll();
                        if (count % 3 == 0) {
                            System.out.println(Thread.currentThread().getName()+":A");
                            count++;
                            TimeUnit.MILLISECONDS.sleep(500);
                        }
                        lock.wait();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"T1");

        Thread t2 = new Thread(() -> {
            try {
                while (true) {
                    synchronized (lock) {
                        lock.notifyAll();
                        if (count % 3 == 1) {
                            System.out.println(Thread.currentThread().getName()+":L");
                            count++;
                            TimeUnit.MILLISECONDS.sleep(500);
                        }
                        lock.wait();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"T2");

        Thread t3 = new Thread(() -> {
            try {
                while (true) {
                    synchronized (lock) {
                        lock.notifyAll();
                        if (count % 3 == 2) {
                            System.out.println(Thread.currentThread().getName()+":L");
                            count++;
                            TimeUnit.MILLISECONDS.sleep(500);
                        }
                        lock.wait();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"T3");

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

        Scanner scan = new Scanner(System.in);
        String input;
        do{
            input = scan.next();
        }while (input == null);
        System.out.println("exit");
        
    }
}

由于需要在输入的时候退出主线程,那么现在将其他三个线程都设置为守护线程。

T1:A
T2:L
T3:L
T1:A
T2:L
T3:L
T1:A
T2:L
T3:L
T1:A
T2:L
T3:L
T1:A
T2:L
T3:L
1
exit

此时输入1,导致所有线程全部退出。

2.ReentrantLock & Condation

public class ThreeThreadTest {

    private static final ReentrantLock lock = new ReentrantLock();

    private static final Condition con1 = lock.newCondition();
    private static final Condition con2 = lock.newCondition();
    private static final Condition con3 = lock.newCondition();

    private static int count = 0;

    public static void main(String[] args) throws InterruptedException{
        Thread t1 = new Thread(() -> {
            lock.lock();
            try {
                while (true) {
                    if((count%3) == 0) {
                        System.out.println(Thread.currentThread().getName()+":A");
                        TimeUnit.MILLISECONDS.sleep(500);
                        con2.signal();
                        count ++;
                    }else {
                        con1.await();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        });
        Thread t2 = new Thread(() -> {
            lock.lock();
            try {
                while (true) {
                    if((count%3) == 1) {
                        System.out.println(Thread.currentThread().getName()+":L");
                        TimeUnit.MILLISECONDS.sleep(500);
                        con3.signal();
                        count ++;
                    }else {
                        con2.await();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        });
        Thread t3 = new Thread(() -> {
            lock.lock();
            try {
                while (true) {
                    if((count%3) == 2) {
                        System.out.println(Thread.currentThread().getName()+":L");
                        TimeUnit.MILLISECONDS.sleep(500);
                        con1.signal();
                        count ++;
                    }else {
                        con3.await();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        });

        t1.setDaemon(true);
        t2.setDaemon(true);
        t3.setDaemon(true);

        t1.start();
        TimeUnit.MILLISECONDS.sleep(100);
        t2.start();
        TimeUnit.MILLISECONDS.sleep(100);
        t3.start();

        Scanner scan = new Scanner(System.in);
        String input;
        do{
            input = scan.next();
        }while (input == null);
        System.out.println("exit");
    }
}

执行结果:

Thread-0:A
Thread-1:L
Thread-2:L
Thread-0:A
Thread-1:L
Thread-2:L
1
exit

同理也可以解决这个问题。

3.LockSupport

import java.util.Scanner;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

public class ThreeThreadTest {

    private static boolean state = true;

    public static void main(String[] args) throws InterruptedException{

        Thread t1 = new Thread(() -> {
            while (true) {
                System.out.println(Thread.currentThread().getName() + ":A");
                LockSupport.park();
            }
        },"T1");
        Thread t2 = new Thread(() -> {
            while (true) {
                System.out.println(Thread.currentThread().getName() + ":L");
                LockSupport.park();
            }
        },"T2");
        Thread t3 = new Thread(() -> {
            while (true) {
                System.out.println(Thread.currentThread().getName() + ":L");
                LockSupport.park();
            }
        },"T3");

        Thread t4 = new Thread(() -> {
            int i=0;
            try {
                while (state) {
                    if((i%3)==0) {
                        LockSupport.unpark(t1);
                    }else if((i%3)==1) {
                        LockSupport.unpark(t2);
                    }else {
                        LockSupport.unpark(t3);
                    }
                    i++;
                    TimeUnit.MILLISECONDS.sleep(500);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        t1.setDaemon(true);
        t2.setDaemon(true);
        t3.setDaemon(true);
        t4.setDaemon(true);
        t1.start();
        TimeUnit.MILLISECONDS.sleep(100);
        t2.start();
        TimeUnit.MILLISECONDS.sleep(100);
        t3.start();
        TimeUnit.MILLISECONDS.sleep(100);
        t4.start();

        Scanner scan = new Scanner(System.in);

        String input;
        do{
            input = scan.next();
        }while (input == null);
    }
}

上述代码输出结果:

T1:A
T2:L
T3:L
T1:A
T2:L
T3:L
T1:A
T2:L
1

可以看到使用LockSupport,不需要使用锁进行配合。
这是最灵活的一种方式。

4.总结

对于线程同步的问题,通过前面的学习,实际上有三种方式可以实现。分别是:

  • synchronized + wait + notify/notifyAll
  • ReentrantLock + Condation:await/signal
  • LockSupport:park / unpark
    以上三种方式都可以实现同步,我们可以根据实际需求可以选择合适的方式来实现。

相关文章

网友评论

    本文标题:一道多线程协同面试题的三种解决方案

    本文链接:https://www.haomeiwen.com/subject/sbroektx.html