1114. 按序打印
给你一个类:
public class Foo {
public void first() { print("first"); }
public void second() { print("second"); }
public void third() { print("third"); }
}
三个不同的线程 A、B、C 将会共用一个 Foo 实例。
线程 A 将会调用 first() 方法
线程 B 将会调用 second() 方法
线程 C 将会调用 third() 方法
请设计修改程序,以确保 second() 方法在 first() 方法之后被执行,third() 方法在 second() 方法之后被执行。
提示:
尽管输入中的数字似乎暗示了顺序,但是我们并不保证线程在操作系统中的调度顺序。
你看到的输入格式主要是为了确保测试的全面性。
示例 1:
输入:nums = [1,2,3]
输出:"firstsecondthird"
解释:
有三个线程会被异步启动。输入 [1,2,3] 表示线程 A 将会调用 first() 方法,线程 B 将会调用 second() 方法,线程 C 将会调用 third() 方法。正确的输出是 "firstsecondthird"。
示例 2:
输入:nums = [1,3,2]
输出:"firstsecondthird"
解释:
输入 [1,3,2] 表示线程 A 将会调用 first() 方法,线程 B 将会调用 third() 方法,线程 C 将会调用 second() 方法。正确的输出是 "firstsecondthird"。
解题思路:
定义一个flag,当flag等于1的时候,first可以执行,当flag等于2的时候,second可以执行,当flag等于3的时候third可以执行,否则就await等待,当first执行过后唤醒second,second执行唤醒third。
class Foo {
public Foo() {
}
private int flag = 1;
private Lock lock = new ReentrantLock();
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
private Condition c3 = lock.newCondition();
public void first(Runnable printFirst) throws InterruptedException {
lock.lock();
try{
while(flag!=1){
c1.await();
}
flag++;
c2.signal();
printFirst.run();
}finally{
lock.unlock();
}
}
public void second(Runnable printSecond) throws InterruptedException {
lock.lock();
try{
while(flag!=2){
c2.await();
}
flag++;
c3.signal();
printSecond.run();
}finally{
lock.unlock();
}
}
public void third(Runnable printThird) throws InterruptedException {
lock.lock();
try{
while(flag!=3){
c3.await();
}
printThird.run();
}finally{
lock.unlock();
}
}
}
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
实例:
- 线程 A 将会调用
foo()
方法,而 - 线程 B 将会调用
bar()
方法
请设计修改程序,以确保 "foobar"
被输出 n
次。
示例 1:
输入:n = 1
输出:"foobar"
解释:这里有两个线程被异步启动。其中一个调用 foo() 方法, 另一个调用 bar() 方法,"foobar" 将被输出一次。
示例 2:
输入:n = 1
输出:"foobar"
解释:这里有两个线程被异步启动。其中一个调用 foo() 方法, 另一个调用 bar() 方法,"foobar" 将被输出一次。
解题思路:
定义state,当state是偶数,foo执行,否则bar执行,foo执行唤醒bar,bar执行唤醒foo
class FooBar {
private int n;
public FooBar(int n) {
this.n = n;
}
private Lock lock = new ReentrantLock();
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
private int state = 0;
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; i++) {
lock.lock();
try{
while(state%2!=0){
c1.await();
}
printFoo.run();
c2.signal();
state++;
}finally{
lock.unlock();
}
}
}
public void bar(Runnable printBar) throws InterruptedException {
for (int i = 0; i < n; i++) {
lock.lock();
try{
while(state%2!=1){
c2.await();
}
c1.signal();
printBar.run();
state++;
}finally{
lock.unlock();
}
}
}
}
1116. 打印零与奇偶数
难度中等124
现有函数 printNumber
可以用一个整数参数调用,并输出该整数到控制台。
- 例如,调用
printNumber(7)
将会输出7
到控制台。
给你类 ZeroEvenOdd
的一个实例,该类中有三个函数:zero
、even
和 odd
。ZeroEvenOdd
的相同实例将会传递给三个不同线程:
- 线程 A: 调用
zero()
,只输出0
- 线程 B: 调用
even()
,只输出偶数 - 线程 C: 调用
odd()
,只输出奇数
修改给出的类,以输出序列 "010203040506..."
,其中序列的长度必须为 2n
。
实现 ZeroEvenOdd
类:
ZeroEvenOdd(int n)
用数字n
初始化对象,表示需要输出的数。void zero(printNumber)
调用printNumber
以输出一个 0 。void even(printNumber)
调用printNumber
以输出偶数。void odd(printNumber)
调用printNumber
以输出奇数。
示例 1:
输入:n = 2
输出:"0102"
解释:三条线程异步执行,其中一个调用 zero(),另一个线程调用 even(),最后一个线程调用odd()。正确的输出为 "0102"。
示例 2:
输入:n = 5
输出:"0102030405"
解题思路:
由题意知打印结果如下->0102030405060708....,所以定义一个flag表示0是否可以打印,state表示是打印奇数还是偶数,每次打印奇数或者偶数之前都会打印一次0,所以在zero方法中判断,如果state是偶数,就唤醒调用even线程,如果是奇数就唤醒调用odd线程。
class ZeroEvenOdd {
private int n;
public ZeroEvenOdd(int n) {
this.n = n;
}
private boolean flag = true;
private int state = 1;
private Lock lock = new ReentrantLock();
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
private Condition c3 = lock.newCondition();
// printNumber.accept(x) outputs "x", where x is an integer.
public void zero(IntConsumer printNumber) throws InterruptedException {
for(int i = 0; i < n; i++){
lock.lock();
try{
while(!flag){
c1.await();
}
printNumber.accept(0);
if(state%2==0){
c2.signal();
}else{
c3.signal();
}
flag=false;
}finally{
lock.unlock();
}
}
}
public void even(IntConsumer printNumber) throws InterruptedException {
for(int i = 2; i <= n; i+=2){
lock.lock();
try{
while(state%2==1){
c2.await();
}
printNumber.accept(state);
c1.signal();
state++;
flag=true;
}finally{
lock.unlock();
}
}
}
public void odd(IntConsumer printNumber) throws InterruptedException {
for(int i = 1; i <= n; i+=2){
lock.lock();
try{
while(state%2==0){
c3.await();
}
printNumber.accept(state);
c1.signal();
state++;
flag=true;
}finally{
lock.unlock();
}
}
}
}
1117. H2O 生成
难度中等102
现在有两种线程,氧 oxygen
和氢 hydrogen
,你的目标是组织这两种线程来产生水分子。
存在一个屏障(barrier)使得每个线程必须等候直到一个完整水分子能够被产生出来。
氢和氧线程会被分别给予 releaseHydrogen
和 releaseOxygen
方法来允许它们突破屏障。
这些线程应该三三成组突破屏障并能立即组合产生一个水分子。
你必须保证产生一个水分子所需线程的结合必须发生在下一个水分子产生之前。
换句话说:
- 如果一个氧线程到达屏障时没有氢线程到达,它必须等候直到两个氢线程到达。
- 如果一个氢线程到达屏障时没有其它线程到达,它必须等候直到一个氧线程和另一个氢线程到达。
书写满足这些限制条件的氢、氧线程同步代码。
示例 1:
输入: water = "HOH"
输出: "HHO"
解释: "HOH" 和 "OHH" 依然都是有效解。
示例 2:
输入: water = "OOHHHH"
输出: "HHOHHO"
解释: "HOHHHO", "OHHHHO", "HHOHOH", "HOHHOH", "OHHHOH", "HHOOHH", "HOHOHH" 和 "OHHOHH" 依然都是有效解。
解题思路
由题意可知,每三个字符必须有两个H和一个O,首先可以定义int类型H,和一个int类型的O,当H小于2的时候加一,并调用releaseHydrogen,当H等于2时则等待,当O小于1时则加一并调用releaseOxygen,等于一时则等待,当H等于2,O等于1的时候则唤醒所有等待的线程,进入新的一轮,并且将H和O设置为0。
class H2O {
public H2O() {
}
private Lock lock = new ReentrantLock();
private Condition c = lock.newCondition();
private int H = 0;
private int O = 0;
public void hydrogen(Runnable releaseHydrogen) throws InterruptedException {
lock.lock();
try{
while(H==2){
c.await();
}
H++;
if(H==2&&O==1){
H = 0;
O = 0;
}
c.signalAll();
releaseHydrogen.run();
}finally{
lock.unlock();
}
}
public void oxygen(Runnable releaseOxygen) throws InterruptedException {
lock.lock();
try{
while(O==1){
c.await();
}
O++;
if(H==2&&O==1){
H = 0;
O = 0;
}
c.signalAll();
releaseOxygen.run();
}finally{
lock.unlock();
}
}
}
1195. 交替打印字符串
编写一个可以从 1 到 n 输出代表这个数字的字符串的程序,但是:
- 如果这个数字可以被 3 整除,输出 "fizz"。
- 如果这个数字可以被 5 整除,输出 "buzz"。
- 如果这个数字可以同时被 3 和 5 整除,输出 "fizzbuzz"。
例如,当 n = 15
,输出: 1, 2, fizz, 4, buzz, fizz, 7, 8, fizz, buzz, 11, fizz, 13, 14, fizzbuzz
。
假设有这么一个类:
class FizzBuzz {
public FizzBuzz(int n) { ... } // constructor
public void fizz(printFizz) { ... } // only output "fizz"
public void buzz(printBuzz) { ... } // only output "buzz"
public void fizzbuzz(printFizzBuzz) { ... } // only output "fizzbuzz"
public void number(printNumber) { ... } // only output the numbers
}
请你实现一个有四个线程的多线程版 FizzBuzz
, 同一个 FizzBuzz
实例会被如下四个线程使用:
- 线程A将调用
fizz()
来判断是否能被 3 整除,如果可以,则输出fizz
。 - 线程B将调用
buzz()
来判断是否能被 5 整除,如果可以,则输出buzz
。 - 线程C将调用
fizzbuzz()
来判断是否同时能被 3 和 5 整除,如果可以,则输出fizzbuzz
。 - 线程D将调用
number()
来实现输出既不能被 3 整除也不能被 5 整除的数字。
解题思路:
如果不满足条件则等待,当有线程满足条件则执行方法并将数字加一,然后唤醒所有线程。继续判断下一个数字。
class FizzBuzz {
private int n;
public FizzBuzz(int n) {
this.n = n;
}
private Lock lock = new ReentrantLock();
private Condition c = lock.newCondition();
private int i = 1;
// printFizz.run() outputs "fizz".
public void fizz(Runnable printFizz) throws InterruptedException {
lock.lock();
try{
while(i <= n){
if(i%3==0&&i%5!=0){
printFizz.run();
i++;
c.signalAll();
}else{
c.await();
}
}
}finally{
lock.unlock();
}
}
// printBuzz.run() outputs "buzz".
public void buzz(Runnable printBuzz) throws InterruptedException {
lock.lock();
try{
while(i <= n){
if(i%3!=0&&i%5==0){
printBuzz.run();
i++;
c.signalAll();
}else{
c.await();
}
}
}finally{
lock.unlock();
}
}
// printFizzBuzz.run() outputs "fizzbuzz".
public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
lock.lock();
try{
while(i <= n){
if(i%3==0&&i%5==0){
printFizzBuzz.run();
i++;
c.signalAll();
}else{
c.await();
}
}
}finally{
lock.unlock();
}
}
// printNumber.accept(x) outputs "x", where x is an integer.
public void number(IntConsumer printNumber) throws InterruptedException {
lock.lock();
try{
while(i <= n){
if(i%3!=0&&i%5!=0){
printNumber.accept(i);
i++;
c.signalAll();
}else{
c.await();
}
}
}finally{
lock.unlock();
}
}
}