多线程-按序打印的4种写法

题目描述:

我们提供了一个类:

public class Foo {

  public void one() { print("one"); }

  public void two() { print("two"); }

  public void three() { print("three"); }

}

三个不同的线程将会共用一个 Foo 实例。

线程 A 将会调用 one() 方法

线程 B 将会调用 two() 方法

线程 C 将会调用 three() 方法

请设计修改程序,以确保 two() 方法在 one() 方法之后被执行,three() 方法在 two() 方法之后被执行。

示例 1:

输入: [1,2,3]

输出: "onetwothree"

解释:

有三个线程会被异步启动。

输入 [1,2,3] 表示线程 A 将会调用 one() 方法,线程 B 将会调用 two() 方法,线程 C 将会调用 three() 方法。

正确的输出是 "onetwothree"。

示例 2:

输入: [1,3,2]

输出: "onetwothree"

解释:

输入 [1,3,2] 表示线程 A 将会调用 one() 方法,线程 B 将会调用 three() 方法,线程 C 将会调用 two() 方法。

正确的输出是 "onetwothree"。

解法1:利用锁、成员变量来控制顺序。

first方法直接打印one,并设置flag = 1,并唤醒其他所有线程。

second方法 如果flag != 1 那么轮询,并等待。当first方法执行完后,那么flag = 1,此时second会执行while块之后的,即打印two,并设置flag = 2。如first执行完后,third方法先执行,因此时flag =1,那么third方法会一直在轮询。

third方法在second执行完后,flag = 2,并唤醒其他所有线程,此时处于wait状态的只有third,因此,在second执行完后即会执行third方法。

class Foo {

    private  Object lock =  new Object();

    private int flag = 0;

    volatile int count = 1;

    public Foo() {

    }

    public void first(Runnable printFirst) throws InterruptedException {

        printFirst.run();

        count++;

        // printFirst.run() outputs "first". Do not change or remove this line.

    }

    public void second(Runnable printSecond) throws InterruptedException {

        while(count != 2);

        printSecond.run();

        count++;

        // printSecond.run() outputs "second". Do not change or remove this line.

    }

    public void third(Runnable printThird) throws InterruptedException {

        while(count != 3);

          printThird.run();

        // printThird.run() outputs "third". Do not change or remove this line.

    }

}

解法2:利用volatile变量

class Foo {

    volatile int count = 1;

    public Foo() {

    }

    public void first(Runnable printFirst) throws InterruptedException {

        printFirst.run();

        count++;

        // printFirst.run() outputs "first". Do not change or remove this line.

    }

    public void second(Runnable printSecond) throws InterruptedException {

        while(count != 2);

        printSecond.run();

        count++;

        // printSecond.run() outputs "second". Do not change or remove this line.

    }

    public void third(Runnable printThird) throws InterruptedException {

        while(count != 3);

          printThird.run();

          count = 1;

       // printThird.run() outputs "third". Do not change or remove this line.

    }

}

解法3:利用CountDownLatch,CountDownLatch是java.util.concurrent包下面的一个工具类,可以用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。 它可以允许一个或者多个线程等待其他线程完成操作。

简单点说,直到CountDownLatch里面计数为0才执行所有线程。

首先初始化两个数量均为1的计数器

first方法,在执行cdla.countDown();之后 cdla 计数为0。

second方法,cdla.await(); 如果cdla里计数不为0,那么会一直阻塞在此,直到cdla计数为0,即first方法执行完之后,才会通过。此时再执行cdlb.countDown();,cdlb计数为0。

third方法cdlb.await();如果cdlb里计数不为0,那么会一直阻塞在此,直到cdlb计数为0,即second方法执行完之后。

class Foo {

    private CountDownLatch a;

    private CountDownLatch b;

    public Foo() {

        a = new CountDownLatch(1);

        b = new CountDownLatch(1);

    }

    public void first(Runnable printFirst) throws InterruptedException {

        printFirst.run();

        a.countDown();

        // printFirst.run() outputs "first". Do not change or remove this line.

    }

    public void second(Runnable printSecond) throws InterruptedException {

        a.await();

        printSecond.run();

        b.countDown();

        // printSecond.run() outputs "second". Do not change or remove this line.

    }

    public void third(Runnable printThird) throws InterruptedException {

          b.await();

          printThird.run();

        // printThird.run() outputs "third". Do not change or remove this line.

    }

}


解法4:利用信号量Semaphore

Semaphore 是 synchronized 的加强版,作用是控制线程的并发数量。

如果我们设置Semaphore 里初始值为0,就是一开始使线程阻塞从而完成其他执行。

原理和CountDownLatch 差不多。

first方法直接释放(初始值为0,是可以释放的)。

second方法在最开始会获取spa,只有first方法执行完之后,才能在此处获取到,即只有first执行完之后才会执行second。并释放spb。

third方法在最开始会获取spb,spb释放是在second执行完之后,因此只有在second执行完之后才会执行third。

class Foo {

    private Semaphore a;

    private Semaphore b;

    public Foo() {

        a = new Semaphore(0);

        b = new Semaphore(0);

    }

    public void first(Runnable printFirst) throws InterruptedException {

        printFirst.run();

        a.release();

        // printFirst.run() outputs "first". Do not change or remove this line.

    }

    public void second(Runnable printSecond) throws InterruptedException {

        a.acquire();

        printSecond.run();

        b.release();

        // printSecond.run() outputs "second". Do not change or remove this line.

    }

    public void third(Runnable printThird) throws InterruptedException {

          b.acquire();

          printThird.run();

        // printThird.run() outputs "third". Do not change or remove this line.

    }

}


测试代码:

public static void main(String[] args) {

Foo foo =new Foo();

    for (int i =0; i <8; i++) {

ExecutorService executor = Executors.newFixedThreadPool(3);

        executor.submit(() -> {

try {

foo.first(() -> {

System.out.println("one");

                });

            }catch (InterruptedException e) {

e.printStackTrace();

            }

});

        executor.submit(() -> {

try {

foo.second(() -> {

System.out.println("two");

                });

            }catch (InterruptedException e) {

e.printStackTrace();

            }

});

        executor.submit(() -> {

try {

foo.third(() -> {

System.out.println("three");

                });

            }catch (InterruptedException e) {

e.printStackTrace();

            }

});

        executor.isShutdown();

    }

}




个人座右铭:主动  行动  思考   反省  总结


你可能感兴趣的:(多线程-按序打印的4种写法)