多线程面试题:交替打印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 的锁机制
相关推荐
专注API从业者36 分钟前
《Go 语言高并发爬虫开发:淘宝商品 API 实时采集与 ETL 数据处理管道》
开发语言·后端·爬虫·golang
Asthenia04121 小时前
Netty writeAndFlush与Pipeline深入分析
后端
欧先生^_^1 小时前
Scala语法基础
开发语言·后端·scala
GetcharZp2 小时前
xterm.js 终端神器到底有多强?用了才知道!
前端·后端·go
尤物程序猿3 小时前
【2025最新Java面试八股】如何在Spring启动过程中做缓存预热?
java·缓存·面试
洞窝技术3 小时前
MYSQL:关于索引你想知道的
后端·mysql
MrWho不迷糊3 小时前
企业级权限系统怎么设计四 —— ABAC模型统一功能权限与数据权限
后端·微服务
落尘2983 小时前
Spring MVC——传递参数的方式
后端
ITCharge3 小时前
Docker 万字教程:从入门到掌握
后端·docker·容器
天天扭码4 小时前
JavaScript 中 apply 和 call 方法的区别与应用场景
前端·javascript·面试