多线程面试题:交替打印ABC的多种实现方法

题目描述

建立三个线程thread1、thread2、thread3,要求三个线程同时运行,并且实现交替打印ABC,即按照 ABCABCABCABC... 的顺序打印。

实现思路

  • 使用 volatile 实现
  • 使用 AtomicInteger 实现
  • 使用 wait/notify 实现
  • 使用 Condition 和 lock 实现
  • 使用 BlockingQueue 实现
  • 使用 Semaphore 实现

使用 volatile 实现

使用 volatile 实现是最容易想到的方式。

java 复制代码
public class VolatileOrderThread {

    static volatile int ticket = 1;

    public static void main(String[] args) {

        Thread thread1 = new Thread(() -> {
            while (true) {
                if (ticket == 1) {
                    System.out.println("A");
                    ticket = 2;
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            while (true) {
                if (ticket == 2) {
                    System.out.println("B");
                    ticket = 3;
                }
            }
        });

        Thread thread3 = new Thread(() -> {
            while (true) {
                if (ticket == 3) {
                    System.out.println("C");
                    ticket = 1;
                }
            }
        });

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

使用 AtomicInteger 实现

使用 AtomicInteger 实现本质上和使用 volatile 是一回事情,在 AtomicInteger 内部也维护了一个 volatile 类型的变量,用来保证共享变量的可见性。

java 复制代码
public class AtomicIntegerOrderThread {

    static AtomicInteger ticket = new AtomicInteger(1);

    public static void main(String[] args) {

        Thread thread1 = new Thread(() -> {
            while (true) {
                if (ticket.get() == 1) {
                    System.out.println("A");
                    ticket.set(2);
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            while (true) {
                if (ticket.get() == 2) {
                    System.out.println("B");
                    ticket.set(3);
                }
            }
        });

        Thread thread3 = new Thread(() -> {
            while (true) {
                if (ticket.get() == 3) {
                    System.out.println("C");
                    ticket.set(1);
                }
            }
        });

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

使用 wait/notify 实现

要实现线程间通信,最原始也是最万能的方法,当然是使用 wait/notify。

java 复制代码
public class WaitNotifyOrderThread {

    static int ticket = 1;

    static final Object object = new Object();

    public static void main(String[] args) {

        Thread thread1 = new Thread(() -> {
            while (true) {
                synchronized (object) {
                    try {
                        while (ticket != 1) {
                            object.wait();
                        }
                        System.out.println("A");
                        ticket = 2;
                        object.notifyAll();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            while (true) {
                synchronized (object) {
                    try {
                        while (ticket != 2) {
                            object.wait();
                        }
                        System.out.println("B");
                        ticket = 3;
                        object.notifyAll();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });

        Thread thread3 = new Thread(() -> {
            while (true) {
                synchronized (object) {
                    try {
                        while (ticket != 3) {
                            object.wait();
                        }
                        System.out.println("C");
                        ticket = 1;
                        object.notifyAll();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

使用 Condition 和 lock 实现

Doug Lea 神一样的男人。

java 复制代码
public class ConditionOrderThread {

    static int ticket = 1;

    static Lock lock = new ReentrantLock();

    static Condition conditionA = lock.newCondition();

    static Condition conditionB = lock.newCondition();

    static Condition conditionC = lock.newCondition();

    public static void main(String[] args) {

        Thread thread1 = new Thread(() -> {
            while (true) {
                lock.lock();
                try {
                    if (ticket != 1) {
                        conditionA.await();
                    }
                    System.out.println("A");
                    ticket = 2;
                    conditionB.signal();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } finally {
                    lock.unlock();
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            while (true) {
                lock.lock();
                try {
                    if (ticket != 2) {
                        conditionB.await();
                    }
                    System.out.println("B");
                    ticket = 3;
                    conditionC.signal();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } finally {
                    lock.unlock();
                }
            }
        });

        Thread thread3 = new Thread(() -> {
            while (true) {
                lock.lock();
                try {
                    if (ticket != 3) {
                        conditionC.await();
                    }
                    System.out.println("C");
                    ticket = 1;
                    conditionA.signal();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } finally {
                    lock.unlock();
                }
            }
        });

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

使用 BlockingQueue 实现

这种方式,本质上也是使用 Condition 和 lock,阻塞队列的 take 方法就是使用 Condition 和 lock 实现的。

java 复制代码
public class BlockingQueueOrderThread {

    static BlockingQueue<Character> queueA = new LinkedBlockingQueue<>(1);

    static BlockingQueue<Character> queueB = new LinkedBlockingQueue<>(1);

    static BlockingQueue<Character> queueC = new LinkedBlockingQueue<>(1);

    public static void main(String[] args) {

        queueA.add('A');

        Thread thread1 = new Thread(() -> {
            while (true) {
                try {
                    Character element = queueA.take();
                    System.out.println(element);
                    queueB.put('B');
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            while (true) {
                try {
                    Character element = queueB.take();
                    System.out.println(element);
                    queueC.put('c');
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        Thread thread3 = new Thread(() -> {
            while (true) {
                try {
                    Character element = queueC.take();
                    System.out.println(element);
                    queueA.put('A');
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

使用 Semaphore 实现

使用 基于 AQS 的 Semaphore(信号量) 也可以轻松的实现。

java 复制代码
public class SemaphoreOrderThread {

    static Semaphore semaphoreA = new Semaphore(1);
    static Semaphore semaphoreB = new Semaphore(0);
    static Semaphore semaphoreC = new Semaphore(0);

    public static void main(String[] args) {

        Thread thread1 = new Thread(() -> {
            while (true) {
                try {
                    semaphoreA.acquire();
                    System.out.println("A");
                    semaphoreB.release();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            while (true) {
                try {
                    semaphoreB.acquire();
                    System.out.println("B");
                    semaphoreC.release();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        Thread thread3 = new Thread(() -> {
            while (true) {
                try {
                    semaphoreC.acquire();
                    System.out.println("C");
                    semaphoreA.release();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

总结

虽然有多种方法,但归根结底只有两种:

  • 一种是基于 Java 原生的锁机制;
  • 一种是基于 CAS 的锁机制
相关推荐
想要成为糕糕手11 分钟前
JavaScript 异步编程完全指南
javascript·面试·promise
Csvn11 分钟前
Linux 文件与目录操作命令(通关版)
后端
cjp56027 分钟前
007. ASP.NET WEB API配置JWT令牌身份认证
后端·asp.net
程序员mine32 分钟前
Web服务密码存储安全详解:从哈希到密钥派生的演进
前端·后端
weixin_3077791334 分钟前
从工具到协作者:AI在后端研发中的流程重构与组织赋能
人工智能·后端·python·算法·自动化
二十画~书生36 分钟前
【LED 点阵驱动】- 面试高频问题全解
面试·职场和发展
fliter38 分钟前
Rust 如何用 Josh 管理跨仓库代码共享
后端
JAVA面经实录9171 小时前
ZooKeeper 面试题完整标准答案(面试背诵版)
分布式·zookeeper·面试
li-xun1 小时前
我给自己的 Django 博客做了一个在线工具箱:从图片压缩到正则测试,尽量都在浏览器本地处理
后端·python·django