【Java多线程手写代码】(一):启动两个线程交替顺序打印奇偶数

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

前言

Java多线程这块是企业面试的热门知识点,面试官也喜欢让候选人手写部分代码,主要为了考察候选人对线程同步机制的理解及使用熟练度。诸如启动两个线程交替打印1~100的奇偶数这种问题张口就来。

面试题分析

其实交替打印奇、偶数就是想启动两个线程实现以下效果:

奇线程:1
偶线程:2
……
奇线程:99
偶线程:100

简单分析一下,首先是两个线程,其次是交替顺序打印。这可以联系到线程之间的通信问题。这时可以想到大致的方向就是加锁,哪个线程拿到锁就打印,然后释放锁让另一个线程获取锁。两个线程轮流拿到锁,实现交替打印的效果。

Object的wait/notify方式

线程通信首先大家能想到的应该是Object类的wait()/notify()机制。

当有多个线程处理等待状态时,notify()是随机唤醒其中一个,如果想唤醒所有,则应该调用notifyAll()方法。

此时我们应该定义两个线程,一个打印奇数,一个打印偶数,两个线程持有同一把对象锁(可以通过构造方法注入),这样就能保证线程之间的同步了。这里有三点需要注意:

  • 共享变量number在两个线程中通过获取到锁之后才能进行操作,我们都知道++操作是非原子性的,但是本例中通过锁保证同一时刻只有一个线程可以操作,所以无需使用AtomicInteger这类的原子变量。
  • 对于奇线程,若当前数字是偶数时(偶线程相反),就调用wait()方法阻塞等待,如果是奇数,则执行打印,并接着自增,然后通过notify()唤醒另一个线程。
  • 若上一步唤醒了线程之后,其实现在奇、偶线程都是在争抢CPU执行权的,如果还是被奇线程抢到了锁,但是判断当前number是偶数,所以会调用wait()操作进入等待。
public class Main {
    private static volatile int number = 1;
    private static final int MAX = 100;

    public static void main(String[] args) {
        Object monitior = new Object();
        Thread oddThread = new Thread(new OddThread(monitior));
        oddThread.setName("奇线程");
        Thread evenThread = new Thread(new EvenThread(monitior));
        evenThread.setName("偶线程");
        oddThread.start();
        evenThread.start();
    }

    /**
     * 奇数线程
     */
    static class OddThread implements Runnable {
        private Object monitor;

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

        @Override
        public void run() {
            while (number < MAX) {
                synchronized (monitor) {
                    while (number % 2 == 0) {
                        try {
                            monitor.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println(Thread.currentThread().getName() + "-->" + number);
                    number++;
                    monitor.notify();
                }
            }
        }
    }

    /**
     * 偶数线程
     */
    static class EvenThread implements Runnable {
        private Object monitor;

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

        @Override
        public void run() {
            while (number <= MAX) {
                synchronized (monitor) {
                    while (number % 2 != 0) {
                        try {
                            monitor.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println(Thread.currentThread().getName() + "-->" + number);
                    number++;
                    monitor.notify();
                }
            }
        }
    }

}

通过一个volatile类型的变量控制

该方式的思路是,线程在volatile变量上无限循环,直到volatile变量变为false。变为false后,线程开始真正地执行业务逻辑,打印数字,最后,需要挂起自己,并修改volatile变量,来唤醒其他线程。

public class Main {
    private static volatile int number = 1;
    private static final int MAX = 100;
    private static volatile boolean flag = true;

    public static void main(String[] args) {
        Thread oddThread = new Thread(new OddThread());
        oddThread.setName("奇线程");
        Thread evenThread = new Thread(new EvenThread());
        evenThread.setName("偶线程");
        oddThread.start();
        evenThread.start();
    }

    /**
     * 奇数线程
     */
    static class OddThread implements Runnable {
        @Override
        public void run() {
            while (number < MAX) {
                while (flag) {
                    System.out.println(Thread.currentThread().getName() + "-->" + number);
                    number++;
                    flag = false;
                }
            }
        }
    }

    /**
     * 偶数线程
     */
    static class EvenThread implements Runnable {
        @Override
        public void run() {
            while (number <= MAX) {
                while (!flag) {
                    System.out.println(Thread.currentThread().getName() + "-->" + number);
                    number++;
                    flag = true;
                }
            }
        }
    }

}

通过原子变量AtomicInteger和闭锁CountDownLatch实现

public class Main {
    private static ExecutorService executorService = new ThreadPoolExecutor(2, 5, 1000L,
            TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1024));

    private static CountDownLatch countDownLatch = new CountDownLatch(2);
    private static volatile boolean flag = false;
    private static AtomicInteger num = new AtomicInteger(1);
    private static final Integer MAX = 100;

    public static void main(String[] args) throws InterruptedException {
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                while (num.get() <= MAX ) {
                    if (!flag) {
                        System.out.println(Thread.currentThread().getName() + "-->" + num.getAndIncrement());
                        flag = true;
                    }
                }
                countDownLatch.countDown();
            }
        });
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                while (num.get() <= MAX) {
                    if (flag) {
                        System.out.println(Thread.currentThread().getName() + "-->" + num.getAndIncrement());
                        flag = false;
                    }
                }
                countDownLatch.countDown();
            }
        });
        countDownLatch.await();
    }
}

我是少侠露飞,咱们下期见

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值