打印零与奇偶数
难度:
标签:
题目描述
代码结果
运行时间: 34 ms, 内存: 17.2 MB
/*
* 思路:
* 1. 与普通Java方法类似,使用信号量控制线程的顺序。
* 2. 由于Java Stream不适合控制线程间同步,这里仍然使用传统方式。
* 3. 可以使用IntStream生成序列以简化打印操作。
*/
import java.util.concurrent.Semaphore;
import java.util.function.IntConsumer;
import java.util.stream.IntStream;
public class ZeroEvenOddStream {
private int n;
private Semaphore semaphoreZero = new Semaphore(1);
private Semaphore semaphoreOddEven = new Semaphore(0);
private volatile boolean isOddTurn = true;
public ZeroEvenOddStream(int n) {
this.n = n;
}
public void zero(IntConsumer printNumber) throws InterruptedException {
IntStream.range(0, n).forEach(i -> {
try {
semaphoreZero.acquire();
printNumber.accept(0);
semaphoreOddEven.release();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
public void even(IntConsumer printNumber) throws InterruptedException {
IntStream.iterate(2, i -> i + 2).limit(n / 2).forEach(i -> {
try {
semaphoreOddEven.acquire();
if (!isOddTurn) {
printNumber.accept(i);
isOddTurn = true;
semaphoreZero.release();
} else {
semaphoreOddEven.release();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
public void odd(IntConsumer printNumber) throws InterruptedException {
IntStream.iterate(1, i -> i + 2).limit((n + 1) / 2).forEach(i -> {
try {
semaphoreOddEven.acquire();
if (isOddTurn) {
printNumber.accept(i);
isOddTurn = false;
semaphoreZero.release();
} else {
semaphoreOddEven.release();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
}
解释
方法:
该题解使用了三个线程和三个锁(zero_lock, odd_lock, even_lock)来协调输出序列。初始时,odd_lock 和 even_lock 被锁定,zero_lock 可用。zero 函数先获取 zero_lock,输出 0,然后根据当前数字的奇偶性释放 odd_lock 或 even_lock。odd 函数获取 odd_lock,输出奇数,然后释放 zero_lock。even 函数获取 even_lock,输出偶数,然后释放 zero_lock。通过这种方式,三个线程交替执行,按照题目要求输出序列。
时间复杂度:
O(n)
空间复杂度:
O(1)
代码细节讲解
🦆
在实现`ZeroEvenOdd`类时,为什么选择使用三个锁来控制线程同步而不是其他同步机制如信号量或条件变量?
▷🦆
为何在初始化时立即锁定`odd_lock`和`even_lock`,而不是在第一次调用`odd()`或`even()`方法时再进行锁定?
▷🦆
该实现中`zero`函数在释放`even_lock`或`odd_lock`后,是否存在线程安全问题,例如可能的race condition?
▷🦆
在`zero`函数中使用`if i % 2 == 0`来决定释放哪一个锁。这种方法是否可能导致在某些情况下线程不同步,尤其是在处理器调度不均或系统负载较高的情况下?
▷相关问题
交替打印 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 = 2 输出:"foobarfoobar" 解释:"foobar" 将被输出两次。
提示:
1 <= n <= 1000