我是少侠露飞。学习塑造人生,技术改变世界。
前言
这是少侠在前段时间的面试中,被快手面试官真实问到过的题目(是真的快手面试官,没有吸引眼球哦)。
问题描述很简单:利用三个线程,第一个线程打印1 2 3 4 5, 第二个线程打印 6 7 8 9 10, 第三个线程打印11 12 13 14 15,然后第一个线程再打印16 17 18 19 20,然后第二个线程接着打印21… 依此类推,直到打印到60。
通过wait/notifyAll机制
/**
* @author Carson
* @date 2020/11/12 21:50
*/
public class Main {
// 数字初始值
private static int num = 1;
private static final int MAX = 60;
// 执行线程的标记字段,1表示线程A执行,2表示线程B执行,3表示线程C执行
// 通过volatile保证可见性
private static volatile int flag = 1;
public static void main(String[] args) {
Object monitor = new Object();
Thread pOne = new Thread(new PrintOne(monitor));
Thread pTwo = new Thread(new PrintTwo(monitor));
Thread pThr = new Thread(new PrintThr(monitor));
pOne.setName("线程A");
pTwo.setName("线程B");
pThr.setName("线程C");
pOne.start();
pTwo.start();
pThr.start();
}
static class PrintOne implements Runnable {
final Object monitor;
public PrintOne(Object monitor) {
this.monitor = monitor;
}
@Override
public void run() {
while (num < MAX) {
synchronized (monitor) {
// 通过一个无限循环验证当前是否该线程A执行,
// 若不是就调用wait方法释放锁
while (flag != 1) {
try {
monitor.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print(Thread.currentThread().getName() + "-->");
for (int i = 0; i < 5; i++) {
System.out.print(num + " ");
num++;
}
// 此处打印只为换行
System.out.println();
// 将flag标记置为B,目的在于唤醒线程B
flag = 2;
// 唤醒其它线程
monitor.notifyAll();
}
}
}
}
static class PrintTwo implements Runnable {
final Object monitor;
public PrintTwo(Object monitor) {
this.monitor = monitor;
}
@Override
public void run() {
while (num < MAX) {
synchronized (monitor) {
while (flag != 2) {
try {
monitor.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print(Thread.currentThread().getName() + "-->");
for (int i = 0; i < 5; i++) {
System.out.print(num + " ");
num++;
}
System.out.println();
// 将flag标记置为C,目的在于唤醒线程C
flag = 3;
monitor.notifyAll();
}
}
}
}
static class PrintThr implements Runnable {
final Object monitor;
public PrintThr(Object monitor) {
this.monitor = monitor;
}
@Override
public void run() {
while (num < MAX) {
synchronized (monitor) {
while (flag != 3) {
try {
monitor.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print(Thread.currentThread().getName() + "-->");
for (int i = 0; i < 5; i++) {
System.out.print(num + " ");
num++;
}
System.out.println();
// 将flag标记置为A,目的在于唤醒线程A
flag = 1;
monitor.notifyAll();
}
}
}
}
}
通过ReentrantLock和Condition实现
这种方法的思路在于:通过一个共享的ReentrantLock
控制三个线程的访问,我们都知道ReentrantLock
是可重入锁。我们可以通过条件变量Condition
实现线程A给线程B发送信号唤醒B,线程B给线程C发送信号唤醒C,这样依次循环执行打印。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Main {
public static void main(String[] args) throws InterruptedException {
// 定义锁
ReentrantLock lock = new ReentrantLock();
// 打印线程A的condition
Condition conditionA = lock.newCondition();
// 打印线程B的condition
Condition conditionB = lock.newCondition();
// 打印线程C的condition
Condition conditionC = lock.newCondition();
// 实例化A线程
Thread printerA = new Thread(new ReentrantLockSyncPrinter(lock, conditionA, conditionB));
// 实例化B线程
Thread printerB = new Thread(new ReentrantLockSyncPrinter(lock, conditionB, conditionC));
// 实例化C线程
Thread printerC = new Thread(new ReentrantLockSyncPrinter(lock, conditionC, conditionA));
printerA.setName("线程A");
printerB.setName("线程B");
printerC.setName("线程C");
printerA.start();
Thread.sleep(100);
printerB.start();
printerC.start();
}
}
class ReentrantLockSyncPrinter implements Runnable {
// 初始值
private static int num = 1;
private static final int MAX = 60;
// 打印次数
private static final int COUNT = 5;
// 打印锁
final ReentrantLock reentrantLock;
// 本线程打印所需的condition
final Condition currCondition;
// 下一个线程打印所需要的condition
final Condition nextCondition;
public ReentrantLockSyncPrinter(ReentrantLock reentrantLock, Condition currCondition, Condition nextCondition) {
this.reentrantLock = reentrantLock;
this.currCondition = currCondition;
this.nextCondition = nextCondition;
}
@Override
public void run() {
// 获取锁,进入临界区
reentrantLock.lock();
try {
while (num < MAX) {
System.out.print(Thread.currentThread().getName() + "-->");
// 连续打印COUNT次
for (int i = 0; i < COUNT; i++) {
//打印字符
System.out.print(num + " ");
num++;
}
System.out.println();
// 使用nextCondition唤醒下一个线程
// 因为只有一个线程在等待,所以signal或者signalAll都可以
nextCondition.signal();
try {
// 本线程让出锁并等待唤醒
currCondition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
// 释放锁
reentrantLock.unlock();
}
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/13482.html