- 交替打印 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" 将被输出一次。
问题分析:
对于上面的问题,我们可以知道,是解决两个线程协作的问题;
分析如下:我们可以知道当前只有两个线程,我记为线程A和线程B,对于这两个线程,分别去执行foo()和bar()方法;我们还能知道,foo方法只有一个线程在调用,bar方法也是只有一个线程在调用,因此foo/bar方法并不存在并发的问题;,所以该问题只是简单的归类为协作问题(用一个状态即可解决,而没必要要用锁的信号量机制,太重了!);
以下代码能成功实现协作的一大原因是volatile修饰了permitFoo;
//自旋 + 让出CPU
class FooBar {
private int n;
public FooBar(int n) {
this.n = n;
}
volatile boolean permitFoo = true;
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; ) {
if(permitFoo) {
printFoo.run(); //这个操作需要在permitFoo=false之前;
i++;
permitFoo = false;
}else{
Thread.yield();//主动让出cpu
}
}
}
public void bar(Runnable printBar) throws InterruptedException {
for (int i = 0; i < n; ) {
if(!permitFoo) {
printBar.run();
i++;
permitFoo = true;
}else{
Thread.yield();
}
}
}
}
那如果说问题升级为一个foo方法存在多并发的场景呢?这又如何解决呢?那就需要同步机制了,这个时候可以引出锁,直接用一个lock锁住对应的代码块就行,或者说直接加一个synchronized关键字加在方法或者同样代码块的方式即可;类似如下
class testJustTwoThread{
ReentrantLock lock = new ReentrantLock();
volatile boolean flag = false; //乐观锁机制可见性
public void pop(){
for (int i = 0; i < 100;) {
lock.lock(); //防止多线程并发访问该代码块
try {
//借助volatile关键字修饰的变量进行线程协作。。
if(flag){
System.out.println("A");
flag = false;
i++;
}else{
//没有执行i++,其实算是一种自旋操作
//主动让出cpu的方式
Thread.yield();
}
}finally {
lock.unlock();
}
}
}
public void push(){
for (int i = 0; i < 100;) {
lock.lock();
try {
if(!flag){
System.out.println("B");
flag = true;
i++;
}else{
//没有执行i++,其实算是一种自旋操作
//主动让出cpu的方式
Thread.yield();
}
}finally {
lock.unlock();
}
}
}
}