1114. 按序打印
我们提供了一个类:
public class Foo {
public void first() {
print("first"); }
public void second() {
print("second"); }
public void third() {
print("third"); }
}
三个不同的线程将会共用一个 Foo 实例。
线程 A 将会调用 first() 方法
线程 B 将会调用 second() 方法
线程 C 将会调用 third() 方法
请设计修改程序,以确保 second() 方法在 first() 方法之后被执行,third() 方法在 second() 方法之后被执行。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/print-in-order
示例 1:
输入: [1,2,3]
输出: “firstsecondthird”
解释:
有三个线程会被异步启动。
输入 [1,2,3] 表示线程 A 将会调用 first() 方法,线程 B 将会调用 second() 方法,线程 C 将会调用 third() 方法。
正确的输出是 “firstsecondthird”。
示例 2:
输入: [1,3,2]
输出: “firstsecondthird”
解释:
输入 [1,3,2] 表示线程 A 将会调用 first() 方法,线程 B 将会调用 third() 方法,线程 C 将会调用 second() 方法。
正确的输出是 “firstsecondthird”。
提示:
尽管输入中的数字似乎暗示了顺序,但是我们并不保证线程在操作系统中的调度顺序。
你看到的输入格式主要是为了确保测试的全面性。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/print-in-order
官方题解
class Foo {
private AtomicInteger firstJobDone=new AtomicInteger(0);
private AtomicInteger secondJobDone=new AtomicInteger(0);
public Foo() {
}
public void first(Runnable printFirst) throws InterruptedException {
// printFirst.run() outputs "first". Do not change or remove this line.
printFirst.run();
//mark the first job as done,by increasing its count
firstJobDone.incrementAndGet();
}
public void second(Runnable printSecond) throws InterruptedException {
while(firstJobDone.get()!=1)
{
// waiting for the first job to be done
}
// printSecond.run() outputs "second". Do not change or remove this line.
printSecond.run();
//mark the second as done,by increasing its count
secondJobDone.incrementAndGet();
}
public void third(Runnable printThird) throws InterruptedException {
while(secondJobDone.get()!=1)
{
//waiting for the second job to be done
}
// printThird.run() outputs "third". Do not change or remove this line.
printThird.run();
}
}
构造执行屏障实现
我们使用一个 Ojbect 对象 lock 实现所有执行屏障的锁对象,两个布尔型对象 firstFinished,secondFinished 保存屏障消除的条件。
作者:pulsaryu
链接:https://leetcode-cn.com/problems/print-in-order/solution/gou-zao-zhi-xing-ping-zhang-shi-xian-
class Foo {
private boolean firstFinished;
private boolean secondFinished;
private Object lock=new Object();
public Foo() {
}
public void first(Runnable printFirst) throws InterruptedException {
synchronized(lock)
{
// printFirst.run() outputs "first". Do not change or remove this line.
printFirst.run();
firstFinished=true;
lock.notifyAll();
}
}
public void second(Runnable printSecond) throws InterruptedException {
synchronized(lock)
{
while(firstFinished==false)
{
lock.wait();//当他拿到锁时,如果第一个线程没执行,他就释放这个锁(这样第一个线程就有机会拿到锁了)
}
// printSecond.run() outputs "second". Do not change or remove this line.
printSecond.run();
secondFinished=true;
lock.notifyAll();
}
}
public void third(Runnable printThird) throws InterruptedException {
synchronized(lock)
{
while(!secondFinished)
{
lock.wait();
}
// printThird.run() outputs "third". Do not change or remove this line.
printThird.run();
//lock.notifyAll();//不用通知其他线程,因为它肯定是最后执行的
}
}
}
1115. 交替打印FooBar
我们提供一个类:
class FooBar {
public void foo() {
for (int i = 0; i < n; i++) {
print("foo");
}
}
public void bar() {
for (int i = 0; i < n; i++) {
print("bar");
}
}
}
两个不同的线程将会共用一个 FooBar 实例。其中一个线程将会调用 foo() 方法,另一个线程将会调用 bar() 方法。
请设计修改程序,以确保 “foobar” 被输出 n 次。
示例 1:
输入: n = 1
输出: “foobar”
解释: 这里有两个线程被异步启动。其中一个调用 foo() 方法, 另一个调用 bar() 方法,“foobar” 将被输出一次。
示例 2:
输入: n = 2
输出: “foobarfoobar”
解释: “foobar” 将被输出两次。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/print-foobar-alternately
本地运行完整代码
package LeetCode.MultiThread;
public class jiaotiFooBar implements Runnable {
FooBar foobar=new FooBar(100);
public static void main(String[] args) {
jiaotiFooBar jiaotiFooBar = new jiaotiFooBar();
new Thread(jiaotiFooBar,"1").start();
new Thread(jiaotiFooBar,"2").start();
}
@Override
public void run() {
if(Thread.currentThread().getName()=="1")
{
try {
foobar.foo();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(Thread.currentThread().getName()=="2")
{
try {
foobar.bar();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class FooBar{
private int n;
boolean fooFlag=false;
boolean barFlag=false;
volatile boolean fooFirstRun=false;
public FooBar(int n) {
this.n = n;
}
public synchronized void foo() throws InterruptedException {
for (int i = 0; i < n; i++) {
if(fooFirstRun)//如果已经打印过foo,打印foo的线程先等待
{
wait();
}
System.out.print("foo");
fooFirstRun=true;
notify();
}
}
public synchronized void bar() throws InterruptedException {
for (int i = 0; i < n; i++) {
if (!fooFirstRun) {
wait();//如果还没打印foo,打印bar的线程先等待
}
System.out.print("bar");
fooFirstRun=false;
notify();
}
}
}
解法2
记套路模板:
1、AtomicInteger 标记量
2、锁资源
3、wait、notify/notifyAll 的使用(变种就是本文中的 await 和 singal)
4、条件阻塞
5、循环条件(一般都是最外层)
class FooBar {
private int n;
private AtomicInteger flag = new AtomicInteger(0);
private ReentrantLock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
public FooBar(int n) {
this.n = n;
}
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; i++) {
lock.lock();
try {
while (flag.get() != 0) {
condition1.await();
}
printFoo.run();
flag.set(1);
condition2.signal();
} finally {
lock.unlock();
}
}
}
public void bar(Runnable printBar) throws InterruptedException {
for (int i = 0; i < n; i++) {
lock.lock();
try {
while (flag.get() != 1) {
condition2.await();
}
printBar.run();
flag.set(0);
condition1.signal();
} finally {
lock.unlock();
}
}
}
}
作者:leetcoder-youzg
链接:https://leetcode-cn.com/problems/print-foobar-alternately/solution/java-yi-fa-tong-tong-mo-fa-by-leetcoder-fhqql/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
1116. 打印零与奇偶数
假设有这么一个类:
class ZeroEvenOdd {
public ZeroEvenOdd(int n) {
... } // 构造函数
public void zero(printNumber) {
... } // 仅打印出 0
public void even(printNumber) {
... } // 仅打印出 偶数
public void odd(printNumber) {
... } // 仅打印出 奇数
}
相同的一个 ZeroEvenOdd 类实例将会传递给三个不同的线程:
线程 A 将调用 zero(),它只输出 0 。
线程 B 将调用 even(),它只输出偶数。
线程 C 将调用 odd(),它只输出奇数。
每个线程都有一个 printNumber 方法来输出一个整数。请修改给出的代码以输出整数序列 010203040506… ,其中序列的长度必须为 2n。
示例 1:
输入:n = 2
输出:“0102”
说明:三条线程异步执行,其中一个调用 zero(),另一个线程调用 even(),最后一个线程调用odd()。正确的输出为 “0102”。
示例 2:
输入:n = 5
输出:“0102030405”
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/print-zero-even-odd
我的解法
自己写了两个小时,不管力扣过不过,先贴在这,至少自己本地测试是没有问题的
package LeetCode.MultiThread;
public class printZero implements Runnable{
ZeroEvenOdd zeroEvenOdd=new ZeroEvenOdd(10);
public static void main(String[] args) {
printZero printZero=new printZero();
new Thread(printZero,"1").start();
new Thread(printZero,"2").start();
new Thread(printZero,"3").start();
}
@Override
public void run() {
if(Thread.currentThread().getName()=="1")
{
try {
zeroEvenOdd.zero();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(Thread.currentThread().getName()=="2") //线程2调用even()
{
try {
zeroEvenOdd.even();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(Thread.currentThread().getName()=="3")
{
try {
zeroEvenOdd.Odd();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class ZeroEvenOdd{
private int n;
boolean firstFinished=false;
boolean secondFinished=false;
boolean thirdFinished=true;//为了满足第一个线程的执行条件,设置为true
boolean hasOddtran=false;//bug:会出现偶数线程比单数线程先执行,这样程序无法正常结束,所以设一个标志位,最开始让奇数线程(Odd)先执行
public ZeroEvenOdd(int n) {
this.n = n;
}
public synchronized void zero() throws InterruptedException {
for (int i =1; i <= n; i++) {
if(!((thirdFinished||secondFinished)&&!firstFinished))//如果不是(第2或第3个线程执行完毕且第一个线程未执行)就释放锁
{
wait();
}
System.out.print(0);
firstFinished=true;
secondFinished=false;
thirdFinished=false;
notifyAll();//因为剩余两个线程,所以要用notifyAll()
}
}
public synchronized void even() throws InterruptedException {
for (int i = 1; i <= n; i++) {
if(!(firstFinished&&!secondFinished)||hasOddtran==false)//如果不是(第一个线程执行完毕,且第二个线程未执行)就释放锁
{
wait();
}
if(i%2==0)//打印偶数
{
System.out.print(i);
secondFinished=true;
firstFinished=false;
thirdFinished=false;
notifyAll();
}
}
}
public synchronized void Odd() throws InterruptedException {
for (int i = 1; i <= n; i++) {
if( !(firstFinished&&!thirdFinished))//如果不是(第一个线程执行完毕,第三个线程未执行)就释放锁
{
wait();
}
if(i%2==1)//打印奇数
{
System.out.print(i);
thirdFinished=true;
secondFinished=false;
firstFinished=false;
hasOddtran=true;
notifyAll();
}
}
}
}
测试结果
力扣测试的话是奇数先打印完,才打印的偶数,而且当测试用例是奇数时,会超时,搞不懂
可以通过的题解
解题思路
下标从1开始数:
0占奇数下标(即i%2 == 1);
偶数占4的倍数的下标(即i/2%2 == 0);
奇数占其它下标(即i/2%2 == 1);
作者:WeitongBai
链接:https://leetcode-cn.com/problems/print-zero-even-odd/solution/100shi-jian-jie-fa-xia-biao-biao-ji-ling-qi-ou-by-/
class ZeroEvenOdd2 {
private int n;
private int i = 1;
public ZeroEvenOdd2(int n) {
this.n = n;
}
// printNumber.accept(x) outputs "x", where x is an integer.
public void zero() throws InterruptedException {
synchronized(this){
while(true){
while (i <= 2 *n && i%2 == 0) wait();
if (i > 2*n) break;
//printNumber.accept(0);
System.out.print(0);
i++;
notifyAll();
}
}
}
public void even() throws InterruptedException {
synchronized(this){
while(true){
while (i <= 2*n && (i%2 == 1 || (i%2 == 0 && i/2%2 == 1))) wait();
if(i > 2*n) break;
// printNumber.accept(i/2);
System.out.print(i/2);
i++;
notifyAll();
}
}
}
public void odd() throws InterruptedException {
synchronized(this){
while(true){
while (i <= 2*n &&(i%2 == 1 || (i%2 == 0 && i/2%2 == 0))) wait();
if(i > 2*n) break;
//printNumber.accept(i/2);
System.out.print(i/2);
i++;
notifyAll();
}
}
}
}
解题思路2
可以通过Thread.yeild()释放CPU资源,
其实本题想表达的意思就是A、B、C三个线程共同的协作,保证:
1.B执行前要A先执行,C执行前要A先执行
2.C要在B执行之前
3.保证输出 n * 2个数(其中有n个0)
说白了就是不管先输出奇数,还是先输出偶数,先输出0。且奇数优先输出于偶数。
那么咱就开始控制这个流程就好了
1.首先执行的是A线程,然后由A线程控制下一个阶段,核心控制
2.由控制器来控制放行的是B还是C。默认
private volatile boolean control = true;
保证C先执行。
3.当C先执行完,将控制器“翻面”,然后回退到A线程去处理。
作者:zong-you-yi-tian
链接:https://leetcode-cn.com/problems/print-zero-even-odd/solution/qi-shi-tong-xian-qian-liang-dao-ti-yi-yang-wo-men-/
来源:力扣(LeetCode)
class ZeroEvenOdd {
private int n;
private volatile int state;
public ZeroEvenOdd(int n) {
this.n = n;
}
public void zero(IntConsumer printNumber) throws InterruptedException {
for (int i = 0; i < n; i++) {
while (state != 0) {
Thread.yield();
}
printNumber.accept(0);
if (i % 2 == 0) {
state = 1;
} else {
state = 2;
}
}
}
public void even(IntConsumer printNumber) throws InterruptedException {
for (int i = 2; i <= n; i += 2) {
while (state != 2) {
Thread.yield();
}
printNumber.accept(i);
state = 0;
}
}
public void odd(IntConsumer printNumber) throws InterruptedException {
for (int i = 1; i <= n; i += 2) {
while (state != 1) {
Thread.yield();
}
printNumber.accept(i);
state = 0;
}
}
}
解题思路3
通过三个信号量来控制
zero方法中的for表示要输出的0个次数,同时用来控制要唤醒偶数还是奇数方法
even方法用来输出偶数同时唤醒zero方法
odd方法用来输出奇数同时唤醒zero方法
作者:dan-xie-6
链接:https://leetcode-cn.com/problems/print-zero-even-odd/solution/xin-hao-liang-jie-jue-by-dan-xie-6/
import java.util.concurrent.Semaphore;
import java.util.function.IntConsumer;
class ZeroEvenOdd {
private int n;
private Semaphore zero = new Semaphore(1);
private Semaphore even = new Semaphore(0);
private Semaphore odd = new Semaphore(0);
public ZeroEvenOdd(int n) {
this.n = n;
}
// printNumber.accept(x) outputs "x", where x is an integer.
public void zero(IntConsumer printNumber) throws InterruptedException {
for (int i=1;i<=n;i++){
zero.acquire();
printNumber.accept(0);
if(i%2==1){
odd.release();
}else{
even.release();
}
}
}
public void even(IntConsumer printNumber) throws InterruptedException {
for (int i=2;i<=n;i+=2){
even.acquire();
printNumber.accept(i);
zero.release();
}
}
public void odd(IntConsumer printNumber) throws InterruptedException {
for (int i=1;i<=n;i+=2){
odd.acquire();
printNumber.accept(i);
zero.release();
}
}
public static void main(String[] args) {
ZeroEvenOdd zeroEvenOdd = new ZeroEvenOdd(6);
new Thread(() -> {
try {
zeroEvenOdd.zero(System.out::print);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
zeroEvenOdd.even(System.out::print);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
zeroEvenOdd.odd(System.out::print);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}