美文网首页程序员技术干货
java如何让多个线程交替执行?

java如何让多个线程交替执行?

作者: stathry | 来源:发表于2018-03-28 11:37 被阅读0次

    曾经有面试官问过这样一个问题,如何让两个线程交替打印奇数和偶数?如何让多个线程交替执行?

    回答这个问题之前需要先了解java多线程的运行机制,线程间通信机制,线程同步问题。

    这个问题我知道的实现方案有两种,一种是基于synchronized和wait/notify,另外一种是基于Lock和Condition.

    1.基于synchronized和wait/notify

    package org.stathry.jdkdeep.concurrent;

    /**

    * 线程间通信-基于Object的wait/notify

    * @date 2017年12月21日

    */

    public class ThreadTurnTest1 {

    private Object lock = new Object();

    private boolean RUN0 = true;

    private static final int LIMIT = 1000;

    public static void main(String[] args) throws InterruptedException {

    final ThreadTurnTest1 o = new ThreadTurnTest1();

    new Thread(new Runnable() {

    @Override

    public void run() {

    try {

    o.m0();

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    }, "t0").start();

    new Thread(new Runnable() {

    @Override

    public void run() {

    try {

    o.m1();

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    }, "t1").start();

    Thread.sleep(10 * 1000);

    }

    private void m1() throws InterruptedException {

    for (int i = 1; i < LIMIT; i += 2) {

    synchronized (lock) {

    if (RUN0) {

    lock.wait();

    }

    System.out.println(Thread.currentThread().getName() + "___" + i);

    RUN0 = true;

    lock.notify();

    }

    }

    }

    private void m0() throws InterruptedException {

    for (int i = 0; i < LIMIT; i += 2) {

    synchronized (lock) {

    if (!RUN0) {

    lock.wait();

    }

    System.out.println(Thread.currentThread().getName() + "___" + i);

    RUN0 = false;

    lock.notify();

    }

    }

    }

    }

    执行结果如下:

    t0___0

    t1___1

    t0___2

    t1___3

    t0___4

    t1___5

    t0___6

    t1___7

    t0___8

    t1___9

    t0___10

    ...

    2.基于Lock和Condition

    基于Lock和Condition可以实现两个以上线程的通信。

    package org.stathry.jdkdeep.concurrent;

    import java.util.concurrent.ExecutorService;

    import java.util.concurrent.Executors;

    import java.util.concurrent.LinkedBlockingQueue;

    import java.util.concurrent.ThreadPoolExecutor;

    import java.util.concurrent.TimeUnit;

    import java.util.concurrent.locks.Condition;

    import java.util.concurrent.locks.Lock;

    import java.util.concurrent.locks.ReentrantLock;

    /**

    * 线程间通信-基于lock的condition

    * @date 2017年12月21日

    */

    public class ThreadTurnTest2 {

    private Lock lock = new ReentrantLock();

    private Condition c0 = lock.newCondition();

    private Condition c1 = lock.newCondition();

    private Condition c2 = lock.newCondition();

    private int status = 0;

    private static final int LIMIT = 10000;

    public static void main(String[] args) throws InterruptedException {

    ExecutorService exec = new ThreadPoolExecutor(3, 30, 60, TimeUnit.SECONDS, new LinkedBlockingQueue(300),

    Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

    final ThreadTurnTest2 o = new ThreadTurnTest2();

    exec.submit(o.createTask(0));

    exec.submit(o.createTask(1));

    exec.submit(o.createTask(2));

    exec.shutdown();

    exec.awaitTermination(5, TimeUnit.MINUTES);

    }

    /**

    * @param name

    * @param i

    */

    private Runnable createTask(final int i) {

    return new Runnable() {

    @Override

    public void run() {

    try {

    m(i);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    };

    }

    private void m(final int n) throws InterruptedException {

    for (int i = n; i < LIMIT; i += 3) {

    lock.lock();

    try {

    switch (n) {

    case 0:

    if (status != 0) {

    c0.await();

    }

    System.out.println(Thread.currentThread().getName() + "___" + i);

    status = 1;

    c1.signal();

    break;

    case 1:

    if (status != 1) {

    c1.await();

    }

    System.out.println(Thread.currentThread().getName() + "___" + i);

    status = 2;

    c2.signal();

    break;

    case 2:

    if (status != 2) {

    c2.await();

    }

    System.out.println(Thread.currentThread().getName() + "___" + i);

    status = 0;

    c0.signal();

    break;

    default:

    break;

    }

    } finally {

    lock.unlock();

    }

    }

    }

    }

    执行结果如下:

    pool-1-thread-1___0

    pool-1-thread-2___1

    pool-1-thread-3___2

    pool-1-thread-1___3

    pool-1-thread-2___4

    pool-1-thread-3___5

    pool-1-thread-1___6

    pool-1-thread-2___7

    pool-1-thread-3___8

    pool-1-thread-1___9

    pool-1-thread-2___10

    pool-1-thread-3___11

    pool-1-thread-1___12

    pool-1-thread-2___13

    pool-1-thread-3___14

    ...

    看完上面两种方式你学会了吗?

    其实这两种方式思路都是类似的,大致可以分三步:

    循环内加锁

    判断共享状态变量,如果状态值表示还没轮到当前线程执行,则调用调用锁对象的wait方法

    等待状态变化的线程被唤醒,执行任务,然后改变状态,然后调用锁对象的notify方法

    再多问几个问题,为什么要加锁呢?因为只有加锁才能保证多线程交替有序执行,否则线程的执行是由操作系统随机调度的,那么执行顺序自然是乱序的。为什么要对同一个对象加锁呢?因为只有对同一对象加锁,才能保证访问该锁的两个线程之间相互通信(不同的对象调用notify不会唤醒处于wait状态的线程),否则即使线程已经获取到锁了,但可能会因为没轮到自己执行而一直处于wait状态。

    如果觉得写的不错,记得点击关注,如果写的不好欢迎批评指正,让我们一起进步!

    相关文章

      网友评论

        本文标题:java如何让多个线程交替执行?

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