public static void main(String[] args) {
Thread a = new Thread(() -> {
Global1.var++;
final ReentrantLock lock = reentrantLock;
lock.lock();
try {
condition.signal();
} finally {
lock.unlock();
}
});
Thread b = new Thread(() -> {
final ReentrantLock lock = reentrantLock;
lock.lock();
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
if (Global1.var == 2) {
//do something;
System.out.println(Thread.currentThread().getName() + " good job");
}
});
a.start();
b.start();
}
}
这个方案使用了Condition对象来实现object的notify、wait效果。当然,这个也有同样的问题。
# 正确解法1--基于错误解法2进行改进
我们看看,前面问题的根源在于,我们线程a,在去通知线程b的时候,有可能线程b还没开始wait,所以此时通知失效。
那么,我们是不是可以先等等,等线程b开始wait了,再去通知呢?
Thread a = new Thread(() -> {
Global1.var++;
final ReentrantLock lock = reentrantLock;
lock.lock();
try {
// 1
while (!reentrantLock.hasWaiters(condition)) {
Thread.yield();
}
condition.signal();
} finally {
lock.unlock();
}
});
1处代码,就是这个思想,在signal之前,判断当前condition上是否有waiter线程,如果没有,就死循环;如果有,才去执行signal。
这个方法实测是可行的。
# 正确解法2
对正确解法1,换一个api,就变成了正确解法2.
Thread a = new Thread(() -> {
Global1.var++;
final ReentrantLock lock = reentrantLock;
lock.lock();
try {
// 1
while (reentrantLock.getWaitQueueLength(condition) == 0) {
Thread.yield();
}
condition.signal();
} finally {
lock.unlock();
}
});
1这里,获取condition上等待队列的长度,如果为0,说明没有等待者,则死循环。
# 正确解法3--基于Semaphore
刚开始,我们初始化一个信号量,state为0\. 线程b去获取信号量的时候,就会阻塞。
然后我们线程a再去释放一个信号量,此时线程b就可以继续执行。
public class Global1 {
public static volatile int var = 1;
public static final Semaphore semaphore = new Semaphore(0);
public static void main(String[] args) {
Thread a = new Thread(() -> {
Global1.var++;
semaphore.release();
});
a.setName("thread a");
Thread b = new Thread(() -> {
try {
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (Global1.var == 2) {
//do something;
System.out.println(Thread.currentThread().getName() + " good job");
}
});
b.setName("thread b");
a.start();
b.start();
}
}
# 正确解法4--基于CountDownLatch
public class Global1 {
public static volatile int var = 1;
public static final CountDownLatch countDownLatch = new CountDownLatch(1);
public static void main(String[] args) {
Thread a = new Thread(() -> {
Global1.var++;
countDownLatch.countDown();
});
a.setName("thread a");
Thread b = new Thread(() -> {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (Global1.var == 2) {
//do something;
System.out.println(Thread.currentThread().getName() + " good job");
}
});
b.setName("thread b");
a.start();
b.start();
}
}
# 正确解法5--基于BlockingQueue
这里使用了ArrayBlockingQueue,其他的阻塞队列也是可以的。
import countdown.CountdownTest;
public class Global1 {
public static volatile int var = 1;
public static final ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(1);
public static void main(String[] args) {
Thread a = new Thread(() -> {
Global1.var++;
arrayBlockingQueue.offer(new Object());
});
a.setName("thread a");
Thread b = new Thread(() -> {
try {
arrayBlockingQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (Global1.var == 2) {
//do something;
System.out.println(Thread.currentThread().getName() + " good job");
}
});
b.setName("thread b");
a.start();
b.start();
}
}
# 正确解法6--基于FutureTask
我们也可以让线程b等待一个task的执行结果;而线程a在执行完修改var为2后,执行该任务,任务执行完成后,线程b就会被通知继续执行。
public class Global1 {
public static volatile int var = 1;
public static final FutureTask futureTask = new FutureTask(new Callable() {
@Override
public Object call() throws Exception {
System.out.println("callable task ");
return null;
}
});
public static void main(String[] args) {
Thread a = new Thread(() -> {
Global1.var++;
futureTask.run();
});
a.setName("thread a");
Thread b = new Thread(() -> {
try {
futureTask.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
if (Global1.var == 2) {
//do something;
System.out.println(Thread.currentThread().getName() + " good job");
}
});
b.setName("thread b");
a.start();
b.start();
}
}
# 正确解法7--基于join
这个可能是最简洁直观的,哈哈。也是群里同学们提供的解法,真的有才!
public class Global1 {
public static volatile int var = 1;
public static void main(String[] args) {
Thread a = new Thread(() -> {
Global1.var++;
});
a.setName("thread a");
Thread b = new Thread(() -> {
try {
a.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (Global1.var == 2) {
//do something;
System.out.println(Thread.currentThread().getName() + " good job");
}
});
b.setName("thread b");
a.start();
b.start();
}
}
# 正确解法8--基于CompletableFuture
这个和第6种类似。都是基于future。
public class Global1 {
public static volatile int var = 1;
public static final CompletableFuture completableFuture =
new CompletableFuture();
public static void main(String[] args) {
Thread a = new Thread(() -> {
Global1.var++;
completableFuture.complete(new Object());
});
a.setName("thread a");
Thread b = new Thread(() -> {
try {
completableFuture.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
if (Global1.var == 2) {
//do something;
System.out.println(Thread.currentThread().getName() + " good job");
}
});
b.setName("thread b");
a.start();
b.start();
}
}
# 非阻塞--正确解法9--忙等待
这种代码量也少,只要线程b在变量为1时,死循环就行了。
public class Global1 {
public static volatile int var = 1;
public static void main(String[] args) {
Thread a = new Thread(() -> {
Global1.var++;
});
a.setName("thread a");
Thread b = new Thread(() -> {
while (var == 1) {
Thread.yield();
}
if (Global1.var == 2) {
//do something;
System.out.println(Thread.currentThread().getName() + " good job");
}
});
b.setName("thread b");
面试题总结
CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】
其它面试题(springboot、mybatis、并发、java中高级面试总结等)
job");
}
});
b.setName(“thread b”);
面试题总结
CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】
其它面试题(springboot、mybatis、并发、java中高级面试总结等)
[外链图片转存中…(img-nbxxM1fK-1630933537230)]
[外链图片转存中…(img-YBqAw524-1630933537232)]