多线程循环打印ABC/ 123的多种方法

一、题目分析

需要明白这道题是考察我们对于线程的控制,所以如果没有丰富的多线程开发经验,手撕这道题是有一些难度的

写这道题之前我们首先要知道多线程的几种实现方式

多线程的实现方式

thread内置方法 Or 实现Runnable,使用ReetrantLock同步锁解决

最为粗暴的一种,直接new Thread(() -> {}),内部使用逻辑实现线程的运行方法,假设我们给这道题设置一个ABC的循环次数100,我们知道每个线程的暂停是通过ReetrantLock锁的lock方法执行同步,然后线程调用wait进入阻塞状态,可以让其他的线程获取lock资源执行;如果发现是想要打印对应字母的线程,需要执行sout输出对应字母,然后调用notifyAll()唤醒其他线程。

注意:上面唤醒线程需要全部唤醒(假设线程数量有3个的话),因此多出来唤醒的部分我们需要用if / while条件对其进行阻塞,这样大体的框架就出来了

// An highlighted block
    ```

// A code block
var foo = ‘bar’;

```javascript
// An highlighted block
public class MultiThreadPrint123 {
    public static int times = 0;

    private static Object lock = new Object();


    public static void main(String[] args) {
        Thread threadA = new Thread(new Thread1());
        Thread threadB = new Thread(new Thread2());
        Thread threadC = new Thread(new Thread3());

        threadA.start();
        threadB.start();
        threadC.start();
    }


    static class Thread1 implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                synchronized (lock) {
                    try {
                        while (times % 3 != 0) {
                            lock.wait();
                        }
                        System.out.println("A");
                        times += 1;
                        lock.notifyAll();

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }

            }
        }
    }

    static class Thread2 implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                synchronized (lock) {
                    try {
                        while (times % 3 != 1) {
                            lock.wait();
                        }
                        System.out.println("B");
                        times += 1;
                        lock.notifyAll();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }
            }
        }
    }

    static class Thread3 implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                synchronized (lock) {
                    try {
                        while (times % 3 != 2) {
                            lock.wait();
                        }
                        System.out.println("C");
                        times += 1;
                        lock.notifyAll();

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }
            }
        }
    }
    }

上面展示了一个用三个线程循环打印ABC的代码,但是如果是循环打印ABCDEFG,需要重复写7、8个线程吗?其实不用,我们可以有更简单的方式实现线程打印,增加Thread中的参数就可以实现了

下面展示一些 内联代码片

// 一个线程交替实现多线程打印
public class MultiThreadSimple {
    //将三个线程简化为一个线程,循环打印ABC
    private static Object lock = new Object();

    private static int cnt = 0;

    static class ThreadSimple implements Runnable {
        private int num;
        private String strPrint;

        public ThreadSimple(int num, String strPrint) {
            this.num = num;
            this.strPrint = strPrint;
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                synchronized (lock) {
                    try {
                        while (cnt % 3 != num) {
                            lock.wait();
                        }
                        System.out.println(strPrint); // 使用参数
                        cnt += 1;
                        lock.notifyAll();

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }

            }
        }
    }

    public static void main(String[] args) {
     new Thread(new ThreadSimple(0,"A") ).start();
        new Thread(new ThreadSimple(1,"B") ).start();
        new Thread(new ThreadSimple(2,"C") ).start();
    }

}

Semaphore的信号量acquire和release实现

话不多说,直接上代码

// Semaphore实现交替打印ABC
public class SemaphoreTest {
    private static Semaphore semaphore1 = new Semaphore(1);
    private static Semaphore semaphore2 = new Semaphore(0);

    private static Semaphore semaphore3 = new Semaphore(0);


    public  static void printABC(String str,Semaphore now, Semaphore next){
        for(int i=0 ;i< 10; i++){
            try{
                now.acquire();
                System.out.println(str);
                next.release();
            }catch (Exception e){
                e.printStackTrace();
            }
        }

    }

    public static void main(String[] args) {
        new Thread(() -> printABC("A",semaphore1, semaphore2)).start();
        new Thread(() -> printABC("B",semaphore2, semaphore3)).start();
        new Thread(() -> printABC("C",semaphore3, semaphore1)).start();
    }
}

可以看出,这种方式比thread+reetrantLock更加简单

Condition的实现方式

实现方式如下,可以看到核心方法为将wait和notifyAll转换为await和signal,好处是可以做特定线程的唤醒。最后因为增加的是reetrantLock锁,所以要在finally代码块中对lock对象进行解锁

// A code block
public class MultiConditionPrintABC {
    private static Lock lock = new ReentrantLock();
    private static Condition conditionA = lock.newCondition();
    private static Condition conditionB = lock.newCondition();
    private static Condition conditionC = lock.newCondition();
    private static int state = 0;

    static class ThreadA implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                lock.lock();
                try {
                    while (state % 3 != 0) {
                        conditionA.await();
                    }
                    System.out.print("A");
                    state++;
                    conditionB.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    static class ThreadB implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                lock.lock();
                try {
                    while (state % 3 != 1) {
                        conditionB.await();
                    }
                    System.out.print("B");
                    state++;
                    conditionC.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    static class ThreadC implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                lock.lock();
                try {
                    while (state % 3 != 2) {
                        conditionC.await();
                    }
                    System.out.print("C");
                    state++;
                    conditionA.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    public static void main(String[] args) {
        new Thread(new ThreadA()).start();
        new Thread(new ThreadB()).start();
        new Thread(new ThreadC()).start();
    }
}