多线程之按顺序打印结果

简介: 前言文本已收录至我的GitHub仓库,欢迎Star:github.com/bin39232820…种一棵树最好的时间是十年前,其次是现在

絮叨


这个是一个很简单的多线程问题,但是小六六一看觉得很简单,但是真正写起来也不是那么简单,说明小六六自己的代码还是写的太少了哈哈


题目


我们提供了一个类:

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"。
复制代码


题解一  synchronized 锁和控制变量


package com.code.thread;
/**
 * 上面方法就是采用一个synchronized wait notifyAll  这些来实现的,但是吧,还需要一个while去自旋,还得靠一个信号量,感觉有点low。
 */
class Foo {
    private  int flag=0;
    private Object lock=new Object();
    public Foo() {
    }
    public void first(Runnable printFirst) throws InterruptedException {
        synchronized(lock){
            while (flag!=0){
                lock.wait();
            }
            // printFirst.run() outputs "first". Do not change or remove this line.
            printFirst.run();
            flag=1;
            lock.notifyAll();
        }
    }
    public void second(Runnable printSecond) throws InterruptedException {
        synchronized (lock) {
            while (flag != 1) {
                lock.wait();
            }
            // printSecond.run() outputs "second". Do not change or remove this line.
            printSecond.run();
            flag = 2;
            lock.notifyAll();
        }
    }
    public void third(Runnable printThird) throws InterruptedException {
        synchronized(lock) {
            while (flag != 2) {
                lock.wait();
            }
            // printThird.run() outputs "third". Do not change or remove this line.
            printThird.run();
            lock.notifyAll();
        }
    }
    public static void main(String[] args) throws Exception {
        final Foo foo = new Foo();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.first(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(1);
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.second(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(2);
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.third(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(3);
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t3.start();
        t2.start();
        t1.start();
    }
}
复制代码


上面方法就是采用一个synchronized wait notifyAll  这些来实现的,但是吧,还需要一个while去自旋,还得靠一个信号量,感觉有点low。


题解二 用CountDownLatch来控制



  • countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。
  • 是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数- 器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。
package com.code.thread;
import java.util.concurrent.CountDownLatch;
public class Foo1 {
    //定义2个countDownLatch
    private CountDownLatch countDownLatchA=new CountDownLatch(1); //说明只要一个线程调用它就放行 ,它是到0就放行
    private CountDownLatch countDownLatchB=new CountDownLatch(1);
    public Foo1() {
    }
    public void first(Runnable printFirst) throws InterruptedException {
            // printFirst.run() outputs "first". Do not change or remove this line.
            printFirst.run();
    }
    public void second(Runnable printSecond) throws InterruptedException {
            // printSecond.run() outputs "second". Do not change or remove this line.
            printSecond.run();
    }
    public void third(Runnable printThird) throws InterruptedException {
            // printThird.run() outputs "third". Do not change or remove this line.
        printThird.run();
    }
    public static void main(String[] args) throws Exception {
        final Foo1 foo = new Foo1();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.first(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(1);
                        }
                    });
                    foo.countDownLatchA.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.countDownLatchA.await();
                    foo.second(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(2);
                        }
                    });
                    foo.countDownLatchB.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                    try {
                        foo.countDownLatchB.await();
                        foo.third(new Runnable() {
                            @Override
                            public void run() {
                                System.out.println(3);
                            }
                        });
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
            }
        });
        t3.start();
        t2.start();
        t1.start();
    }
}
复制代码


方法三 信号量


基于信号量的解题思路

  • Semaphore 是什么?
  • Semaphore 是一个计数信号量,必须由获取它的线程释放。
package com.code.thread;
import java.util.concurrent.Semaphore;
/**
 *
 * 基于信号量的解题思路
 * Semaphore 是什么?
 * Semaphore 是一个计数信号量,必须由获取它的线程释放。
 *
 * 常用于限制可以访问某些资源的线程数量,例如通过 Semaphore 限流。
 */
public class Foo2 {
    //初始化Semaphore为0的原因:
    // 如果这个Semaphore为零,如果另一线程调用(acquire)这个Semaphore就会产生阻塞,
    // 便可以控制second和third线程的执行
    private Semaphore spa=new Semaphore(0) ;
    private Semaphore spb=new Semaphore(0);
    public Foo2() {
    }
    public void first(Runnable printFirst) throws InterruptedException {
        printFirst.run();
        spa.release();
    }
    public void second(Runnable printSecond) throws InterruptedException {
        // printSecond.run() outputs "second". Do not change or remove this line.
        spa.acquire();
        printSecond.run();
        spb.release();
    }
    public void third(Runnable printThird) throws InterruptedException {
        // printThird.run() outputs "third". Do not change or remove this line.
        spb.acquire();
        printThird.run();
    }
    public static void main(String[] args) throws Exception {
        final Foo2 foo = new Foo2();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.first(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(1);
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.second(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(2);
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.third(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(3);
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t3.start();
        t2.start();
        t1.start();
    }
}
复制代码


解题四 LockSupport


归根结底,LockSupport调用的Unsafe中的native代码:

  • public native void unpark(Thread jthread);
  • public native void park(boolean isAbsolute, long time);
  • 两个函数声明清楚地说明了操作对象:park函数是将当前Thread阻塞,而unpark函数则是将另一个Thread唤醒。
package com.code.thread;
import java.util.concurrent.locks.LockSupport;
/**'
 * 归根结底,LockSupport调用的Unsafe中的native代码:
 * public native void unpark(Thread jthread);
 * public native void park(boolean isAbsolute, long time);
 * 两个函数声明清楚地说明了操作对象:park函数是将当前Thread阻塞,而unpark函数则是将另一个Thread唤醒。
 *
 * 与Object类的wait/notify机制相比,park/unpark有两个优点:1. 以thread为操作对象更符合阻塞线程的直观定义;2. 操作更精准,可以准确地唤醒某一个线程(notify随机唤醒一个线程,notifyAll唤醒所有等待的线程),增加了灵活性。
 */
public class Foo3 {
    static Thread t1=null,t2=null,t3=null;
    public Foo3() {
    }
    public void first(Runnable printFirst) throws InterruptedException {
        printFirst.run();
    }
    public void second(Runnable printSecond) throws InterruptedException {
        // printSecond.run() outputs "second". Do not change or remove this line.
        printSecond.run();
    }
    public void third(Runnable printThird) throws InterruptedException {
        // printThird.run() outputs "third". Do not change or remove this line.
        printThird.run();
    }
        public static void main(String[] args) throws Exception {
            final Foo3 foo = new Foo3();
             t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        foo.first(new Runnable() {
                            @Override
                            public void run() {
                                System.out.println(1);
                            }
                        });
                        LockSupport.unpark(t2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
             t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    LockSupport.park();
                    try {
                        foo.second(new Runnable() {
                            @Override
                            public void run() {
                                System.out.println(2);
                            }
                        });
                        LockSupport.unpark(t3);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
             t3 = new Thread(new Runnable() {
                @Override
                public void run() {
                    LockSupport.park();
                    try {
                        foo.third(new Runnable() {
                            @Override
                            public void run() {
                                System.out.println(3);
                            }
                        });
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            t3.start();
            t2.start();
            t1.start();
    }
}



相关文章
|
Java
java有3个独立的线程,一个只会输出A,一个只会输出L,一个只会输出I。在三个线程同时启动的情况下,如何让它们按顺序打印ALIALI。
java有3个独立的线程,一个只会输出A,一个只会输出L,一个只会输出I。在三个线程同时启动的情况下,如何让它们按顺序打印ALIALI。
107 1
|
5月前
|
算法 安全 Java
三种方法教你实现多线程交替打印ABC,干货满满!
本文介绍了多线程编程中的经典问题——多线程交替打印ABC。通过三种方法实现:使用`wait()`和`notify()`、`ReentrantLock`与`Condition`、以及`Semaphore`。每种方法详细讲解了实现步骤和代码示例,帮助读者理解和掌握线程间的同步与互斥,有效解决并发问题。适合不同层次的开发者学习参考。
189 11
|
7月前
|
算法 Java 大数据
Java多线程中顺序打印
Java多线程中顺序打印
104 2
Java多线程中顺序打印
|
消息中间件 移动开发 自然语言处理
多线程知识:三个线程如何交替打印ABC循环100次
synchronized是Java中的一个关键字,用于实现对共享资源的互斥访问。wait和notify是Object类中的两个方法,用于实现线程间的通信。wait方法会让当前线程释放锁,并进入等待状态,直到被其他线程唤醒。notify方法会唤醒一个在同一个锁上等待的线程。
184 1
|
8月前
线程间通信实例之轮流打印ABC
线程间通信实例之轮流打印ABC
65 0
三个线程循环顺序打印
三个线程循环顺序打印
88 0
三个线程按顺序打印ABC?十二种做法,深入多线程同步通信机制
大家好,我是老三,这篇文章分享一道非常不错的题目:三个线程按序打印ABC。 很多读者朋友应该都觉得这道题目不难,这次给大家带来十二种做法,一定有你没有见过的新姿势。
两个线程按顺序打印1~100的几种实现
两个线程按顺序打印1~100的几种实现
191 0
|
缓存 Java 测试技术
让线程按顺序执行8种方法
  本文使用了7中方法实现在多线程中让线程按顺序运行的方法,涉及到多线程中许多常用的方法,不止为了知道如何让线程按顺序运行,更是让读者对多线程的使用有更深刻的了解。 使用的方法如下:
155 0
LeetCode(多线程)- 1114. 按序打印
LeetCode(多线程)- 1114. 按序打印
148 0