文本已收录至我的GitHub仓库,欢迎Star:https://github.com/bin392328206/six-finger
种一棵树最好的时间是十年前,其次是现在
我知道很多人不玩qq了,但是怀旧一下,欢迎加入六脉神剑Java菜鸟学习群,群聊号码:549684836 鼓励大家在技术的路上写博客
这个是一个很简单的多线程问题,但是小六六一看觉得很简单,但是真正写起来也不是那么简单,说明小六六自己的代码还是写的太少了哈哈
我们提供了一个类:
public class Foo {
public void one() { print("one"); }
public void two() { print("two"); }
public void three() { print("three"); }
}
三个不同的线程将会共用一个 Foo 实例。
请设计修改程序,以确保 two() 方法在 one() 方法之后被执行,three() 方法在 two() 方法之后被执行。
示例 1:
输入: [1,2,3]
输出: "onetwothree"
解释:
有三个线程会被异步启动。
输入 [1,2,3] 表示线程 A 将会调用 one() 方法,线程 B 将会调用 two() 方法,线程 C 将会调用 three() 方法。
正确的输出是 "onetwothree"。
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。
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();
}
}
基于信号量的解题思路
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调用的Unsafe中的native代码:
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();
}
}
其实解法有很多,如果多线程不熟悉的小伙伴,可以去我的github博客,先熟悉一下。这题应该算简单的题型了。
好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是真粉。
创作不易,各位的支持和认可,就是我创作的最大动力,我们下篇文章见
六脉神剑 | 文 【原创】如果本篇博客有任何错误,请批评指教,不胜感激 !