Suppose we have a class:
public class Foo {
public void first() { print("first"); }
public void second() { print("second"); }
public void third() { print("third"); }
}
The same instance of Foo
will be passed to three different threads. Thread A will call first()
, thread B will call second()
, and thread C will call third()
. Design a mechanism and modify the program to ensure that second()
is executed after first()
, and third()
is executed after second()
.
Example 1:
Input: [1,2,3]
Output: "firstsecondthird"
Explanation: There are three threads being fired asynchronously. The input [1,2,3] means thread A calls first(), thread B calls second(), and thread C calls third(). "firstsecondthird" is the correct output.
Example 2:
Input: [1,3,2]
Output: "firstsecondthird"
Explanation: The input [1,3,2] means thread A calls first(), thread B calls third(), and thread C calls second(). "firstsecondthird" is the correct output.
Note:
We do not know how the threads will be scheduled in the operating system, even though the numbers in the input seems to imply the ordering. The input format you see is mainly to ensure our tests' comprehensiveness.
要求按顺序执行线程顺序打印出:first -> second -> third
主要用wait()和notifyAll()方法实现:
wait()方法:释放CPU执行权,释放对象锁
notifyAll(): 唤醒所有正在等待该对象锁的线程。
class Foo {
// 定义两个标识控制second 和 third 运行顺序
private boolean run2;
private boolean run3;
final private byte[] lock = new byte[1]; // 对象锁
public Foo() {
this.run2 = false;
this.run3 = false;
}
public void first(Runnable printFirst) throws InterruptedException {
synchronized (lock) {
// printFirst.run() outputs "first". Do not change or remove this line.
printFirst.run();
run2 = true; // 设置second 方法为true跳过while循环直接打印结果
lock.notifyAll();
}
}
public void second(Runnable printSecond) throws InterruptedException {
synchronized (lock) {
while (!run2) {
lock.wait(); // run2 为 false 时,线程等待,释放cpu执行权同时释放对象锁
}
// printSecond.run() outputs "second". Do not change or remove this line.
printSecond.run();
run3 = true; // 设置third方法为true跳过while循环直接打印结果
lock.notifyAll();
}
}
public void third(Runnable printThird) throws InterruptedException {
synchronized (lock) {
while (!run3) {
lock.wait(); // run3 为 false 时,线程等待,释放cpu执行权同时释放对象锁
}
// printThird.run() outputs "third". Do not change or remove this line.
printThird.run();
}
}
}
自己做完又到了参考大佬们代码的时候:
大佬用的是java current 包的Semaphore类:
import java.util.concurrent.*;
class Foo {
Semaphore run2, run3;
public Foo() {
run2 = new Semaphore(0);
run3 = new Semaphore(0);
}
public void first(Runnable printFirst) throws InterruptedException {
printFirst.run();
run2.release();
}
public void second(Runnable printSecond) throws InterruptedException {
run2.acquire();
printSecond.run();
run3.release();
}
public void third(Runnable printThird) throws InterruptedException {
run3.acquire();
printThird.run();
}
}
关于Semaphore类,大佬的解释如下:
https://leetcode.com/problems/print-in-order/discuss/332890/Java-Basic-semaphore-solution-8ms-36MB
- Semaphore is a bowl of marbles (or locks in this case). If you need a marble, and there are none, you wait. You wait until there is one marble and then you take it. If you release(), you will add one marble to the bowl (from thin air). If you release(100), you will add 100 marbles to the bowl (from thin air).
- The thread calling third() will wait until the end of second() when it releases a '3' marble. The second() will wait until the end of first() when it releases a '2' marble. Since first() never acquires anything, it will never wait. There is a forced wait ordering.
- With semaphores, you can start out with 1 marble or 0 marbles or 100 marbles. A thread can take marbles (up until it's empty) or put many marbles (out of thin air) at a time
Semaphore 可以控制允许活动的线程数量也就是线程并发数量。new 的时候需要传递一个参数代表同一时间允许活动的线程数量。线程需要通过acquire()方法获取许可,如果允许活动线程数量为0,则调用acquire()之后,线程会进入等待队列,直到调用release()释放许可。上边解法new Semaphore(0),证明线程需要等待直到允许活动的线程数大于0,release() 方法可以解放一个线程,如果在此之前允许活动的线程数量为0,则会凭空添加一个允许活动的线程。