美文网首页
java 多线程面试题4个线程按顺序打印ABCD

java 多线程面试题4个线程按顺序打印ABCD

作者: lesliefang | 来源:发表于2021-06-13 09:21 被阅读0次

    4个线程,一个打印A,一个打印B,一个打印C,一个打印D,要求按 ABCDABCD 的顺序打印

    多线程同步,首先想到的就是 synchronized 和 wait notify 机制。

    import java.util.concurrent.CountDownLatch;
    
    public class Main {
    
        final static int N = 4;
        static int nextNum = 1;
    
        public static void main(String[] args) {
    
            Object o1 = new Object();
            Object o2 = new Object();
            Object o3 = new Object();
            Object o4 = new Object();
    
            CountDownLatch countDownLatch = new CountDownLatch(4);
    
            new Thread(() -> {
                for (int i = 0; i < N; i++) {
                    synchronized (o1) {
                        while (nextNum != 1) {
                            try {
    //                            System.out.println("o1 wait");
                                o1.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                    System.out.println("A");
                    synchronized (o2) {
                        nextNum = 2;
    //                    System.out.println("o2 notifyAll");
                        o2.notifyAll();
                    }
                }
                countDownLatch.countDown();
            }).start();
    
            new Thread(() -> {
                for (int i = 0; i < N; i++) {
                    synchronized (o2) {
                        while (nextNum != 2) {
                            try {
    //                            System.out.println("o2 wait");
                                o2.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                    System.out.println("B");
                    synchronized (o3) {
                        nextNum = 3;
    //                    System.out.println("o3 notifyAll");
                        o3.notifyAll();
                    }
                }
                countDownLatch.countDown();
            }).start();
    
            new Thread(() -> {
                for (int i = 0; i < N; i++) {
                    synchronized (o3) {
                        while (nextNum != 3) {
                            try {
    //                            System.out.println("o3 wait");
                                o3.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                    synchronized (o4) {
                        System.out.println("C");
                        nextNum = 4;
    //                    System.out.println("o4 notifyAll");
                        o4.notifyAll();
                    }
                }
                countDownLatch.countDown();
            }).start();
    
            new Thread(() -> {
                for (int i = 0; i < N; i++) {
                    synchronized (o4) {
                        while (nextNum != 4) {
                            try {
    //                            System.out.println("o4 wait");
                                o4.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                    synchronized (o1) {
                        System.out.println("D");
                        nextNum = 1;
    //                    System.out.println("o1 notifyAll");
                        o1.notifyAll();
                    }
                }
                countDownLatch.countDown();
            }).start();
    
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    4 个线程,根据条件分别阻塞在不同的 object 锁上

    我只用一个 Object 锁可以吗???当然可以只用一个锁,4个线程同时竞争一个锁,然后同时唤醒再竞争,再附加一个条件变量就行了。代码如下:

    import java.util.concurrent.CountDownLatch;
    
    public class Main {
    
        final static int N = 4;
        static int nextNum = 1;
    
        public static void main(String[] args) {
    
            Object o1 = new Object();
    
            CountDownLatch countDownLatch = new CountDownLatch(4);
    
            new Thread(() -> {
                for (int i = 0; i < N; i++) {
                    synchronized (o1) {
                        while (nextNum != 1) {
                            try {
                                o1.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("A");
                        nextNum = 2;
                        o1.notifyAll();
                    }
                }
                countDownLatch.countDown();
            }).start();
    
            new Thread(() -> {
                for (int i = 0; i < N; i++) {
                    synchronized (o1) {
                        while (nextNum != 2) {
                            try {
                                o1.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("B");
                        nextNum = 3;
                        o1.notifyAll();
                    }
                }
                countDownLatch.countDown();
            }).start();
    
            new Thread(() -> {
                for (int i = 0; i < N; i++) {
                    synchronized (o1) {
                        while (nextNum != 3) {
                            try {
    //                            System.out.println("o3 wait");
                                o1.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("C");
                        nextNum = 4;
                        o1.notifyAll();
                    }
                }
                countDownLatch.countDown();
            }).start();
    
            new Thread(() -> {
                for (int i = 0; i < N; i++) {
                    synchronized (o1) {
                        while (nextNum != 4) {
                            try {
                                o1.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("D");
                        nextNum = 1;
                        o1.notifyAll();
                    }
                }
                countDownLatch.countDown();
            }).start();
    
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    notifyAll 会唤醒所有等待线程,所有线程再同时竞争锁。notify 会随机唤醒一个等待线程。随机唤醒一个线程,被唤醒的线程条件不一定满足,又会进入等待,在高并发场景下性能不好。这就是为什么又出现了 Condition 机制。

    我用 synchronized 方法可以吗???当然可以,synchronized 方法默认会锁住当前对象,就不用 new 一个对象锁了。

    下面看看 Condition 实现

    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Main {
    
        final static int N = 4;
        static int nextNum = 1;
    
        public static void main(String[] args) {
    
            ReentrantLock lock = new ReentrantLock();
            Condition condition1 = lock.newCondition();
            Condition condition2 = lock.newCondition();
            Condition condition3 = lock.newCondition();
            Condition condition4 = lock.newCondition();
    
            CountDownLatch countDownLatch = new CountDownLatch(4);
    
            new Thread(() -> {
                for (int i = 0; i < N; i++) {
                    lock.lock();
                    try {
                        while (nextNum != 1) {
                            try {
    //                            System.out.println("condition1 await");
                                condition1.await();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("A");
                        nextNum = 2;
    //                    System.out.println("condition2 signalAll");
                        condition2.signalAll();
                    } finally {
                        lock.unlock();
                    }
                }
                countDownLatch.countDown();
            }).start();
    
            new Thread(() -> {
                for (int i = 0; i < N; i++) {
                    lock.lock();
                    try {
                        while (nextNum != 2) {
                            try {
    //                            System.out.println("condition2 await");
                                condition2.await();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("B");
                        nextNum = 3;
    //                    System.out.println("condition3 signalAll");
                        condition3.signalAll();
                    } finally {
                        lock.unlock();
                    }
                }
                countDownLatch.countDown();
            }).start();
    
            new Thread(() -> {
                for (int i = 0; i < N; i++) {
                    lock.lock();
                    try {
                        while (nextNum != 3) {
                            try {
    //                            System.out.println("condition3 await");
                                condition3.await();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("C");
                        nextNum = 4;
    //                    System.out.println("condition4 signalAll");
                        condition4.signalAll();
                    } finally {
                        lock.unlock();
                    }
                }
                countDownLatch.countDown();
            }).start();
    
            new Thread(() -> {
                for (int i = 0; i < N; i++) {
                    lock.lock();
                    try {
                        while (nextNum != 4) {
                            try {
    //                            System.out.println("condition4 await");
                                condition4.await();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("D");
                        nextNum = 1;
    //                    System.out.println("condition1 signalAll");
                        condition1.signalAll();
                    } finally {
                        lock.unlock();
                    }
                }
                countDownLatch.countDown();
            }).start();
    
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    ReentrantLock 就等价于 Object 锁。condition.await() 等价于 object.wait(), condition.signal() 等价于 object.notify()。

    我可以只用一个条件变量吗???当然可以,那样和上面只用一个对象锁没什么区别。

    一个ReentrantLock 绑定多个 Condition 对象意味着什么???为什么用 Condition 写生产者消费者效率高???

    synchronized方法 或 Object锁 都只有一个等待队列,所有阻塞的线程都挂在当前对象 或一个 object 实例上。 notify 会随机唤醒队列中的一个线程,而唤醒的线程不一定满足条件,所以又进入等待,这样效率低了一点。

    而 ReentrantLock 绑定多个 Condition 对象, 每个 Condition 对象上都有一个等待队列,我们可以只唤醒一个Condition 对象上等待的线程 。也就是 Condition 能实现粒度更细的锁。


    看别人写的信号量机制

    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.Semaphore;
    
    public class Main {
    
        final static int N = 4;
    
        public static void main(String[] args) {
    
            Semaphore semaphore1 = new Semaphore(1);
            Semaphore semaphore2 = new Semaphore(0);
            Semaphore semaphore3 = new Semaphore(0);
            Semaphore semaphore4 = new Semaphore(0);
    
            CountDownLatch countDownLatch = new CountDownLatch(4);
    
            new Thread(() -> {
                for (int i = 0; i < N; i++) {
                    try {
                        semaphore1.acquire();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("A");
                    semaphore2.release();
                }
                countDownLatch.countDown();
            }).start();
    
            new Thread(() -> {
                for (int i = 0; i < N; i++) {
                    try {
                        semaphore2.acquire();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("B");
                    semaphore3.release();
                }
                countDownLatch.countDown();
            }).start();
    
            new Thread(() -> {
                for (int i = 0; i < N; i++) {
                    try {
                        semaphore3.acquire();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("C");
                    semaphore4.release();
                }
                countDownLatch.countDown();
            }).start();
    
            new Thread(() -> {
                for (int i = 0; i < N; i++) {
                    try {
                        semaphore4.acquire();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("D");
                    semaphore1.release();
                }
                countDownLatch.countDown();
            }).start();
    
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    我可以只用一个信号量吗???当然可以,那样还得附加一个条件变量,跟只用一个 object 锁也差不多。

    相关文章

      网友评论

          本文标题:java 多线程面试题4个线程按顺序打印ABCD

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