求一个 Java 面试题的最佳实践 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Cbdy
V2EX    Java

求一个 Java 面试题的最佳实践

  •  
  •   Cbdy 2021-01-25 12:24:37 +08:00 via Android 5226 次点击
    这是一个创建于 1727 天前的主题,其中的信息可能已经有所发展或是发生改变。

    三个线程,分别输出 a 、b 、c,各输出 100 次,要求按 abc 的顺序输出

    期待输出为:abcabcabcabc...

    48 条回复    2021-02-02 11:26:02 +08:00
    Yi23
        1
    Yi23  
       2021-01-25 12:47:01 +08:00
    第一反应可以使用 lock.condition 实现,3 个线程每个线程打印自己的字符,然后唤醒下一个线程。
    线程 1 的方法类似如下
    类似这样
    ```
    lock.lock();
    while (n != a) {
    condition1.await(); // 线程 1 阻塞
    }
    // 输出 n
    n = b; // 修改 n=b 然后唤醒线程 2
    condition2.signal();
    lock.unlock();
    ```
    chenshun00
        2
    chenshun00  
       2021-01-25 13:00:29 +08:00
    public static void main(String[] args) {

    final Object xx1 = new Object();
    final Object xx2 = new Object();
    final Object xx3 = new Object();
    Thread a = new Thread(() -> {

    try {
    synchronized (xx1) {
    xx1.wait();
    }
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    int a1 = 0;
    while (a1 < 100) {
    System.out.print("a");
    a1++;

    synchronized (xx2) {
    xx2.notify();
    }
    try {
    synchronized (xx1) {
    xx1.wait();
    }
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    });
    a.setDaemon(true);
    a.setName("aaa");
    Thread b = new Thread(() -> {
    try {
    synchronized (xx2) {
    xx2.wait();
    }
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    int b1 = 0;
    while (b1 < 100) {
    System.out.print("b");
    b1++;
    synchronized (xx3) {
    xx3.notify();
    }
    try {
    synchronized (xx2) {
    xx2.wait();
    }
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    });
    b.setDaemon(true);
    b.setName("bbb");
    Thread c = new Thread(() -> {
    try {
    synchronized (xx3) {
    xx3.wait();
    }
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    int c1 = 0;
    while (c1 < 100) {
    System.out.println("c");
    c1++;
    synchronized (xx1) {
    xx1.notify();
    }
    try {
    synchronized (xx3) {
    xx3.wait();
    }
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    });
    c.setDaemon(true);
    c.setName("ccc");
    a.start();
    b.start();
    c.start();

    synchronized (xx1) {
    xx1.notify();
    }

    try {
    Thread.sleep(100L);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }

    }
    mawerss1
        3
    mawerss1  
       2021-01-25 13:06:56 +08:00
    public class Volatile implements Runnable{


    private volatile int state = 0;

    public static void main(String[] args) throws InterruptedException {
    for (int i = 0; i < 3; i++) {
    Thread thread = new Thread(new Volatile());
    thread.start();
    thread.join();
    }
    }

    @Override
    public void run() {
    int count = 0;
    while (true) {
    if (count >= 100) {
    break;
    }
    if (state == 0) {
    System.out.println("a");
    state = 1;
    }
    if (state == 1) {
    System.out.println("b");
    state = 2;
    }
    if (state == 2) {
    System.out.println("c");
    state = 0;
    }
    count++;
    }
    }
    }
    chenshun00
        4
    chenshun00  
       2021-01-25 13:12:25 +08:00
    @mawerss1 好像偏题了
    mytudan
        5
    mytudan  
       2021-01-25 13:26:22 +08:00
    class Abc {
    private int n;
    // 标志位,控制执行顺序,0 执行 a,1 执行 b 2 执行 c
    private volatile int type;
    // 锁标志
    private final Object object;

    public Abc(int n) {
    this.n = n;
    object = new Object();
    type = 0;
    }

    public void a(Runnable a) throws InterruptedException {
    for (int i = 0; i < n; i++) {
    synchronized (object) {
    while (!(type == 0)) {
    object.wait();
    }
    a.run();
    type = 1;
    object.notifyAll();
    }
    }
    }

    public void b(Runnable b) throws InterruptedException {
    for (int i = 0; i < n; i++) {
    synchronized (object) {
    while (!(type == 1)) {
    object.wait();
    }
    b.run();
    type = 2;
    object.notifyAll();
    }
    }
    }

    public void c(Runnable c) throws InterruptedException {
    for (int i = 0; i < n; i++) {
    synchronized (object) {
    while (!(type == 2)) {
    object.wait();
    }
    c.run();
    type = 0;
    object.notifyAll();
    }
    }
    }

    }
    mawerss1
        6
    mawerss1  
       2021-01-25 13:34:29 +08:00
    @chenshun00 哈哈 全错了
    chendy
        7
    chendy  
       2021-01-25 13:35:06 +08:00
    生产者-消费者模型
    a 生产 b 消费,b 生产 c 消费,c 生产 a 消费
    leafre
        8
    leafre  
       2021-01-25 13:41:17 +08:00
    ReentrantLock
    hitmanx
        9
    hitmanx  
       2021-01-25 13:46:59 +08:00
    把它看成三个独立的生产者消费者,用独立的 sync objects
    woshiaha
        10
    woshiaha  
       2021-01-25 13:49:04 +08:00   1
    老题了 用一个原子数当锁 每次有输出原子数都加一 抢到锁才可以输出
    a 线程只在原子数%3 为 0 时输出
    b 线程只在原子数%3 为 1 时输出
    c 线程只在原子数%3 为 2 时输出
    mytudan
        11
    mytudan  
       2021-01-25 13:49:26 +08:00
    我上面那个是照着 leetcode 上的题改的
    mawerss1
        12
    mawerss1  
       2021-01-25 13:51:31 +08:00
    @chenshun00 state 变量改成 static,join 方法删掉,改成用 CountDownLatch
    yazinnnn
        13
    yazinnnn  
       2021-01-25 13:56:11 +08:00   2
    有个问题,这个多线程有什么意义?
    Vendettar
        14
    Vendettar  
       2021-01-25 14:09:30 +08:00
    ```java
    public static void main(String[] args) {
    ReentrantLock lock = new ReentrantLock(true);
    for (int i = 0; i < 3; i++) {
    int order = i;
    new Thread(()->{
    for (int j = 0; j < 100; j++) {
    lock.lock();
    System.out.println(Thread.currentThread().getName() + ":" + (char)('a'+order));
    lock.unlock();
    }
    }, "Thread-" + i).start();
    }
    }
    ```
    Vendettar
        15
    Vendettar  
       2021-01-25 14:10:16 +08:00
    直接一个公平锁 new ReentrantLock(true)按顺序来就可以
    Takamine
        16
    Takamine  
       2021-01-25 14:13:50 +08:00 via Android
    刚撸的一个,应该算是比较传统的解法吧。
    https://paste.ubuntu.com/p/zbDMK6qJQm/
    micean
        17
    micean  
       2021-01-25 14:14:46 +08:00
    每个线程调用这个类就行了
    class ABCPrinter{
    int[] arr = new int[]{0, 0, 0};
    int i = 0;

    public synchronized void addChar(char ch){
    arr[(int)(ch - 'a')]++;
    print();
    }

    void print(){
    while(arr[i] > 0){

    打印((char)(arr[i] + 'a'));
    arr[i]--;
    i = (i + 1)%3

    }
    }

    }
    ocean1477
        18
    ocean1477  
       2021-01-25 14:14:54 +08:00
    @Vendettar 直接不对。
    Rorysky
        19
    Rorysky  
       2021-01-25 14:19:28 +08:00
    synchronized 一把梭
    Vendettar
        21
    Vendettar  
       2021-01-25 14:20:52 +08:00
    @ocean1477 咋不对呢 我这里输出是对的啊..
    ocean1477
        22
    ocean1477  
       2021-01-25 14:24:08 +08:00
    @Vendettar 这个是无法保证顺序的,1,2,3 三个线程可以同时进,争抢一个锁。
    Vendettar
        23
    Vendettar  
       2021-01-25 14:28:08 +08:00
    @ocean1477 公平锁不会争抢 你输出一下试试 顺序不会乱
    ocean1477
        24
    ocean1477  
       2021-01-25 14:29:29 +08:00
    @Vendettar 我这会试了下,会乱的,多试几次。
    Thread-0:a
    Thread-0:a
    Thread-0:a
    Thread-0:a
    Thread-0:a
    Thread-1:b
    Thread-2:c
    Thread-0:a
    Thread-1:b
    Thread-2:c
    Thread-0:a
    Thread-1:b
    Thread-2:c
    Thread-0:a
    Thread-1:b
    ocean1477
        25
    ocean1477  
       2021-01-25 14:32:03 +08:00
    @Vendettar 看来老兄要重新看下公平锁了,公平锁≠顺序
    Vendettar
        26
    Vendettar  
       2021-01-25 14:33:34 +08:00
    @ocean1477 热 我试出来了 的确会乱
    ocean1477
        27
    ocean1477  
       2021-01-25 14:38:19 +08:00
    @Vendettar 哈哈,我无意指出,抱歉。
    kx5d62Jn1J9MjoXP
        28
    kx5d62Jn1J9MjoXP  
       2021-01-25 14:39:16 +08:00
    用三个 Semaphore 就行了吧
    Vendettar
        29
    Vendettar  
       2021-01-25 14:42:26 +08:00
    @ocean1477 感谢指出
    sczero
        30
    sczero  
       2021-01-25 14:43:56 +08:00
    public static void main(String[] args) throws InterruptedException {
    final AtomicReference<String> tag = new AtomicReference<>("a");
    final int total = 100;
    new Thread(() -> {
    int count = 0;
    while (count < total) {
    if (tag.get().equals("a")) {
    System.out.print("a");
    tag.set("b");
    count++;
    }
    }
    }).start();
    new Thread(() -> {
    int count = 0;
    while (count < total) {
    if (tag.get().equals("b")) {
    System.out.print("b");
    tag.set("c");
    count++;
    }
    }
    }).start();
    new Thread(() -> {
    int count = 0;
    while (count < total) {
    if (tag.get().equals("c")) {
    System.out.println("c");
    tag.set("a");
    count++;
    }
    }
    }).start();
    }
    kx5d62Jn1J9MjoXP
        31
    kx5d62Jn1J9MjoXP  
       2021-01-25 14:52:58 +08:00   1
    public static void main(String[] arg) {
    Semaphore[] res = {
    new Semaphore(1),
    new Semaphore(0),
    new Semaphore(0),
    };

    for (int i = 0; i < 3; i++) {
    Semaphore current = res[i];
    Semaphore next = res[(i + 1) % 3];
    final char ch = (char) (i + 'a');
    new Thread(new Runnable() {
    @Override
    public void run() {
    for (int j = 0; j < 100; j++) {
    try {
    current.acquire();
    System.out.print(ch);
    next.release();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }

    }
    }
    }).start();
    }

    }
    Cyron
        32
    Cyron  
       2021-01-25 15:01:38 +08:00
    简单公平锁实现
    ```
    public class ReentrantLockDemo implements Runnable {

    private static ReentrantLock lock = new ReentrantLock(true);

    private String content;

    public ReentrantLockDemo(String content) {
    this.cOntent= content;
    }

    @Override
    public void run() {
    while (true) {
    try {
    lock.lock();
    System.out.println(content);
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    } finally {
    lock.unlock();
    }
    }
    }

    public static void main(String[] args) {
    ReentrantLockDemo a = new ReentrantLockDemo("a");
    ReentrantLockDemo b = new ReentrantLockDemo("b");
    ReentrantLockDemo c = new ReentrantLockDemo("c");
    Thread threadA = new Thread(a);
    Thread threadB = new Thread(b);
    Thread threadC = new Thread(c);
    threadA.start();
    threadB.start();
    threadC.start();
    }
    }
    ```
    zzh7982
        33
    zzh7982  
       2021-01-25 15:10:55 +08:00
    不用加锁,join 就行了,刚测试了一把 没问题

    https://gist.github.com/zzh7982/249883f379b50c5ae50eacd48736b5e2
    kikione
        34
    kikione  
       2021-01-25 15:17:07 +08:00
    我想到两个方案,第一个是加锁。 第二个是 join,b 等 a ,c 等 a 。
    sczero
        35
    sczero  
       2021-01-25 15:20:33 +08:00
    @zzh7982 新建了 300 个线程......至于吗....
    zzh7982
        36
    zzh7982  
       2021-01-25 15:24:55 +08:00   1
    @sczero 啊..这... 300 个线程都能控制顺序 这不显得水平高吗[dog]
    kikione
        37
    kikione  
       2021-01-25 15:26:17 +08:00
    for (int i=0;i<100;i++){
    Thread1 thread1 = new Thread1();
    Thread2 thread2 = new Thread2();
    Thread3 thread3 = new Thread3();
    thread1.start();
    thread1.join();
    thread2.start();
    thread2.join();
    thread3.start();
    }
    sampeng
        38
    sampeng  
       2021-01-25 15:34:23 +08:00   1
    都是有锁方案啊。。我提一个无锁和竞争的方案
    三个队列。分别收 a,b,c 。
    输出就是轮询这 3 个队列就好了。。一定都是有序的
    johnson666
        39
    johnson666  
       2021-01-25 16:05:58 +08:00
    原子变量

    public class Test {
    public static void main(String[] args) {
    AtomicInteger at = new AtomicInteger();
    for (int i = 0; i < 3; i++) {
    MyThread t = new MyThread(at, i);
    t.start();
    }
    }
    }

    class MyThread extends Thread {
    private AtomicInteger at;
    private int index;

    public MyThread(AtomicInteger at, int index) {
    this.at = at;
    this.index = index;
    }

    @Override
    public void run() {
    while(true) {
    if(at.get() % 3 == index) {
    System.out.print((char)('a' + index));
    at.incrementAndGet();
    }
    }
    }
    }
    Youen
        40
    Youen  
       2021-01-25 16:53:22 +08:00
    YoongRii
        41
    YoongRii  
       2021-01-25 17:14:22 +08:00
    用原子变量%3 这种解法比较常见,但是需要线程忙等,提供一种用线程池方式实现的思路:

    public static void main(String[] args) throws InterruptedException {
    ExecutorService executorService1 = Executors.newSingleThreadExecutor();
    ExecutorService executorService2 = Executors.newSingleThreadExecutor();
    ExecutorService executorService3 = Executors.newSingleThreadExecutor();

    Runnable[] rs = new Runnable[3];

    rs[0] = () -> {
    System.out.println("A");
    executorService2.submit(rs[1]);
    };

    rs[1] = () -> {
    System.out.println("B");
    executorService2.submit(rs[2]);
    };

    rs[2] = new Runnable() {
    private int a = 1;

    public void run() {
    System.out.println("C");
    if (a++ < 100)
    executorService3.submit(rs[0]);
    }
    };

    executorService1.submit(rs[0]);
    }
    donggexiongdi
        42
    donggexiongdi  
       2021-01-25 17:19:22 +08:00
    public void printAbc(int count) {
    for (int i = 0; i < count; i++) {
    CompletableFuture.runAsync(() -> {
    System.out.println("A");
    }).thenRun(() -> {
    System.out.println("B");
    }).thenRun(() -> {
    System.out.println("C");
    });
    System.out.println("-------");
    }
    }
    donggexiongdi
        43
    donggexiongdi  
       2021-01-25 17:22:06 +08:00
    我擦 不对
    cubecube
        44
    cubecube  
       2021-01-26 01:26:51 +08:00
    @sampeng
    1.队列也需要锁,无锁队列的话,如果某个线程输入太慢,跟不上,consumer 也得等待?
    2.另外,题目说得三个线程个,consumer 还得偷线程或者揉进到生产者线程的执行过程中。

    以上条件要全写对,比状态量的复杂。
    sampeng
        45
    sampeng  
       2021-01-26 09:34:19 +08:00
    @cubecube
    1.我说的无所锁方案是代码不需要明确的写锁的逻辑。也是一个比较自然的做法。而且队列的这种方式并没有资源竞争,只是 consumer 等待而已。
    2.三个线程难道就不能有主线程了?谁起的这 3 个线程呢?当然是主线程上直接循环啊。又不是生产环境。
    Vendettar
        46
    Vendettar  
       2021-01-26 11:04:01 +08:00
    @Cyron 三个线程首次启动抢锁的话(3 线程都没有 sleep 过),a 抢 1b 抢 2 就没问题,如果 b 抢 1 那后面 99 次都是 b 先输出了
    fantastM
        47
    fantastM  
       2021-01-27 14:30:52 +08:00
    #10 说的方案是可行的,并且是基于 CAS 无锁的

    https://gist.github.com/fantasticmao/f78ae0016a81877cf5019d9c22c81c73
    SkyLine7
        48
    SkyLine7  
       2021-02-02 11:26:02 +08:00
    @woshiaha 是的
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2565 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 23ms UTC 04:14 PVG 12:14 LAX 21:14 JFK 00:14
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86