LeetCode刷题之旅【多线程篇-3】中等: 1115. 交替打印FooBar
程序员文章站
2022-05-05 15:17:26
...
2019-11-20
目录
题目:
解题1
class FooBar {
private int n;
private static Object lock = new Object();
private static volatile Boolean flag = true;
public FooBar(int n) {
this.n = n;
}
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; i++) {
synchronized (lock){
if (!flag){
lock.wait();
}
// printBar.run() outputs "bar". Do not change or remove this line.
printFoo.run();
flag = false;
lock.notify();
}
}
}
public void bar(Runnable printBar) throws InterruptedException {
for (int i = 0; i < n; i++) {
synchronized (lock){
if (flag){
lock.wait();
}
// printBar.run() outputs "bar". Do not change or remove this line.
printBar.run();
flag = true;
lock.notify();
}
}
}
public static void main(String[] args) {
FooBar fooBar = new FooBar(2);
Runnable runnable1 = new Runnable() {
@Override
public void run() {
try {
fooBar.foo(new Runnable() {
@Override
public void run() {
System.out.print("foo");
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Runnable runnable2 =new Runnable() {
@Override
public void run() {
try {
fooBar.bar(new Runnable() {
@Override
public void run() {
System.out.print("bar");
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread1 = new Thread(runnable1);
thread1.start();
Thread thread2 = new Thread(runnable2);
thread2.start();
}
}
- 对象同步锁:确保foo和bar方法只会被一个线程执行
- 线程休眠与唤醒:wait/notify
//对象同步锁用法推荐
synchronized (obj) {
while (<condition does not hold>)
obj.wait();
... // Perform action appropriate to condition
}
解题2:对象锁
分析: 将printFoo操作和printBar操作分别加锁,来保证两者串行发生,设置一个boolean类型变量fooTurn来指明当前操作的轮次,当其为True时,打印foo,当其为False时,打印bar。每个线程在同步代码块内部,判断当前轮次和自己的操作是否符合,若符合,则执行打印操作,若不符合,则放弃锁。
class FooBar {
private int n;
private boolean fooTurn = true;
private Object lock = new Object();
public FooBar(int n) {
this.n = n;
}
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; i++) {
synchronized(lock) {
if (!fooTurn) lock.wait();
fooTurn = false;
// printFoo.run() outputs "foo". Do not change or remove this line.
printFoo.run();
lock.notifyAll();
}
}
}
public void bar(Runnable printBar) throws InterruptedException {
for (int i = 0; i < n; i++) {
synchronized(lock) {
if (fooTurn) lock.wait();
fooTurn = true;
// printBar.run() outputs "bar". Do not change or remove this line.
printBar.run();
lock.notifyAll();
}
}
}
}
作者:copyreadmachine
链接:https://leetcode-cn.com/problems/print-foobar-alternately/solution/java-suo-by-copyreadmachine/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
解题3:信号量
- 使用信号量,acquire 就是 值+1
release 就是 值-1
简单理解就是当值是0,就可以继续执行,不为零就等待变为0后再执行
public class FooBar {
private int n;
//here is the full path, or maybe cann't compile in leetcode.
java.util.concurrent.Semaphore semaphoreFoo=new java.util.concurrent.Semaphore(0);
java.util.concurrent.Semaphore semaphoreBar=new java.util.concurrent.Semaphore(0);
public FooBar(int n) {
this.n = n;
}
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; i++) {
printFoo.run();
//由于下面阻塞了,所以这里变为0,下面的方法就能继续执行
semaphoreBar.release();
//这里让他等一会,等到bar()执行完
semaphoreFoo.acquire();
}
}
public void bar(Runnable printBar) throws InterruptedException {
for (int i = 0; i < n; i++) {
// 进来先变为1,就会等上面的release()使他变为0,才进行,所以肯定在foo之后。
semaphoreBar.acquire();
printBar.run();
//bar()执行完了,就让foo()继续。
semaphoreFoo.release();
}
}
}
-
Java 并发包中的信号量 Semaphore 实际上是一个功能完毕的计数信号量,从概念上讲,它维护了一个许可集合,对控制一定资源的消费与回收有着很重要的意义。Semaphore 可以控制某个资源被同时访问的任务数,它通过acquire()获取一个许可,release()释放一个许可。如果被同时访问的任务数已满,则其他 acquire 的任务进入等待状态,直到有一个任务被 release 掉,它才能得到许可。
- 就是 Semaphore 仅仅是对资源的并发访问的任务数进行监控,而不会保证线程安全,因此,在访问的时候,要自己控制线程的安全访问。
上一篇: 菠菜的功效与作用 身边这6类人千万不能吃
下一篇: 多线程面试题-1-按序打印