【Java多线程手写代码】(三):快手面试官让我手写代码实现三个线程按序输出1 2 3 4 5, 6 7 8 9 10, 11 12 13 14 15…

导读:本篇文章讲解 【Java多线程手写代码】(三):快手面试官让我手写代码实现三个线程按序输出1 2 3 4 5, 6 7 8 9 10, 11 12 13 14 15…,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

我是少侠露飞。学习塑造人生,技术改变世界。

前言

这是少侠在前段时间的面试中,被快手面试官真实问到过的题目(是真的快手面试官,没有吸引眼球哦)。

问题描述很简单:利用三个线程,第一个线程打印1 2 3 4 5, 第二个线程打印 6 7 8 9 10, 第三个线程打印11 12 13 14 15,然后第一个线程再打印16 17 18 19 20,然后第二个线程接着打印21… 依此类推,直到打印到60。

通过wait/notifyAll机制

/**
 * @author Carson
 * @date 2020/11/12 21:50
 */
public class Main {
    // 数字初始值
    private static int num = 1;
    private static final int MAX = 60;
    // 执行线程的标记字段,1表示线程A执行,2表示线程B执行,3表示线程C执行
    // 通过volatile保证可见性
    private static volatile int flag = 1;

    public static void main(String[] args) {
        Object monitor = new Object();
        Thread pOne = new Thread(new PrintOne(monitor));
        Thread pTwo = new Thread(new PrintTwo(monitor));
        Thread pThr = new Thread(new PrintThr(monitor));

        pOne.setName("线程A");
        pTwo.setName("线程B");
        pThr.setName("线程C");

        pOne.start();
        pTwo.start();
        pThr.start();

    }

    static class PrintOne implements Runnable {
        final Object monitor;

        public PrintOne(Object monitor) {
            this.monitor = monitor;
        }

        @Override
        public void run() {
            while (num < MAX) {
                synchronized (monitor) {
                	// 通过一个无限循环验证当前是否该线程A执行,
                	// 若不是就调用wait方法释放锁
                    while (flag != 1) {
                        try {
                            monitor.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.print(Thread.currentThread().getName() + "-->");
                    for (int i = 0; i < 5; i++) {
                        System.out.print(num + " ");
                        num++;
                    }
                    // 此处打印只为换行
                    System.out.println();
                    // 将flag标记置为B,目的在于唤醒线程B
                    flag = 2;
                    // 唤醒其它线程
                    monitor.notifyAll();
                }
            }
        }
    }

    static class PrintTwo implements Runnable {
        final Object monitor;

        public PrintTwo(Object monitor) {
            this.monitor = monitor;
        }

        @Override
        public void run() {
            while (num < MAX) {
                synchronized (monitor) {
                    while (flag != 2) {
                        try {
                            monitor.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.print(Thread.currentThread().getName() + "-->");
                    for (int i = 0; i < 5; i++) {
                        System.out.print(num + " ");
                        num++;
                    }
                    System.out.println();
                    // 将flag标记置为C,目的在于唤醒线程C
                    flag = 3;
                    monitor.notifyAll();
                }
            }
        }
    }

    static class PrintThr implements Runnable {
        final Object monitor;

        public PrintThr(Object monitor) {
            this.monitor = monitor;
        }

        @Override
        public void run() {
            while (num < MAX) {
                synchronized (monitor) {
                    while (flag != 3) {
                        try {
                            monitor.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.print(Thread.currentThread().getName() + "-->");
                    for (int i = 0; i < 5; i++) {
                        System.out.print(num + " ");
                        num++;
                    }
                    System.out.println();
                    // 将flag标记置为A,目的在于唤醒线程A
                    flag = 1;
                    monitor.notifyAll();
                }
            }
        }
    }

}

通过ReentrantLock和Condition实现

这种方法的思路在于:通过一个共享的ReentrantLock控制三个线程的访问,我们都知道ReentrantLock是可重入锁。我们可以通过条件变量Condition实现线程A给线程B发送信号唤醒B,线程B给线程C发送信号唤醒C,这样依次循环执行打印。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        // 定义锁
        ReentrantLock lock = new ReentrantLock();
        // 打印线程A的condition
        Condition conditionA = lock.newCondition();
        // 打印线程B的condition
        Condition conditionB = lock.newCondition();
        // 打印线程C的condition
        Condition conditionC = lock.newCondition();
        // 实例化A线程
        Thread printerA = new Thread(new ReentrantLockSyncPrinter(lock, conditionA, conditionB));
        // 实例化B线程
        Thread printerB = new Thread(new ReentrantLockSyncPrinter(lock, conditionB, conditionC));
        // 实例化C线程
        Thread printerC = new Thread(new ReentrantLockSyncPrinter(lock, conditionC, conditionA));

        printerA.setName("线程A");
        printerB.setName("线程B");
        printerC.setName("线程C");

        printerA.start();
        Thread.sleep(100);
        printerB.start();
        printerC.start();

    }

}

class ReentrantLockSyncPrinter implements Runnable {
    // 初始值
    private static int num = 1;
    private static final int MAX = 60;
    // 打印次数
    private static final int COUNT = 5;
    // 打印锁
    final ReentrantLock reentrantLock;
    // 本线程打印所需的condition
    final Condition currCondition;
    // 下一个线程打印所需要的condition
    final Condition nextCondition;

    public ReentrantLockSyncPrinter(ReentrantLock reentrantLock, Condition currCondition, Condition nextCondition) {
        this.reentrantLock = reentrantLock;
        this.currCondition = currCondition;
        this.nextCondition = nextCondition;
    }

    @Override
    public void run() {
        // 获取锁,进入临界区
        reentrantLock.lock();
        try {
            while (num < MAX) {
                System.out.print(Thread.currentThread().getName() + "-->");
                // 连续打印COUNT次
                for (int i = 0; i < COUNT; i++) {
                    //打印字符
                    System.out.print(num + " ");
                    num++;
                }
                System.out.println();
                // 使用nextCondition唤醒下一个线程
                // 因为只有一个线程在等待,所以signal或者signalAll都可以
                nextCondition.signal();
                try {
                    // 本线程让出锁并等待唤醒
                    currCondition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } finally {
            // 释放锁
            reentrantLock.unlock();
        }
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/13482.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!