力扣多线程测试题-循环打印FooBar

此博客为力扣多线程测试题,针对各答案进行解析补充知识点,可先自测:原测试题链接

需求:

两个不同的线程将会共用一个 FooBar 实例:
线程 A 将会调用 foo() 方法
线程 B 将会调用 bar() 方法
请设计修改程序,以确保 “foobar” 被输出 n 次。
例输入:n=1,例输出:foobar
例输入:n=5,例输出:foobarfoobarfoobarfoobarfoobar

待修改代码:

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");}}
}

一、使用sleep()方法

仅仅使用sleep()方法,判断标志值,即可进行多线程更替,此处展示main()方法,后续不展示

public class FooBar {private int n;private int fCount;private int bCount;public FooBar(int n) {this.n = n;}public void foo(Runnable printFoo) throws InterruptedException {for (int i = 0; i < n; i++) {while (fCount != bCount) {Thread.sleep(1);}printFoo.run();fCount++;}}public void bar(Runnable printBar) throws InterruptedException {for (int i = 0; i < n; i++) {while (fCount == bCount) {Thread.sleep(1);}printBar.run();bCount++;}}public static void main(String[] args) throws InterruptedException {FooBar fooBar = new FooBar(5);Thread fooThread = new Thread(new Runnable() {@Overridepublic void run() {try {fooBar.foo(new Runnable() {@Overridepublic void run() {System.out.print("foo");}});} catch (InterruptedException e) {e.printStackTrace();}}});Thread barThread = new Thread(new Runnable() {@Overridepublic void run() {try {fooBar.bar(new Runnable() {@Overridepublic void run() {System.out.print("bar" + "\n");}});} catch (InterruptedException e) {e.printStackTrace();}}});fooThread.start();barThread.start();}}

二、volatile修饰符

 volatile boolean flag = true;public void foo(Runnable printFoo) throws InterruptedException {for (int i = 0; i < n;) {if (flag) {printFoo.run();flag = false;i++;}}}public void bar(Runnable printBar) throws InterruptedException {for (int i = 0; i < n;) {if (!flag) {printBar.run();flag = true;i++;}}}

volatile修饰符:修饰共享变量后,每个线程要操作变量时会从主内存中将变量拷贝到本地内存作为副本,当线程操作变量副本并写回主内存后,会通过 CPU 总线嗅探机制告知其他线程该变量副本已经失效,需要重新从主内存中读取。保证了不同线程对共享变量操作的可见性,也就是说一个线程修改了 volatile 修饰的变量,当修改后的变量写回主内存时,其他线程能立即看到最新值。

详细可参考:volatile修饰符

三、synchronized关键字

 volatile boolean flag = true;private final Object object = new Object();public void foo(Runnable printFoo) throws InterruptedException {for (int i = 0; i < n; i++) {synchronized (object) {while(!flag) {object.wait();}printFoo.run();flag = false;object.notifyAll();}}}public void bar(Runnable printBar) throws InterruptedException {for (int i = 0; i < n; i++) {synchronized (object) {while(flag) {object.wait();}printBar.run();flag = true;object.notifyAll();}}}

详细可参考:synchronized

四、可重入锁 + Condition类

 Lock lock = new ReentrantLock(true);private final Condition condition = lock.newCondition();volatile boolean flag = true;public void foo(Runnable printFoo) throws InterruptedException {for (int i = 0; i < n; i++) {lock.lock();try {while (!flag) {condition.await();}printFoo.run();flag = false;condition.signal();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}}public void bar(Runnable printBar) throws InterruptedException {for (int i = 0; i < n; i++) {lock.lock();try {while (flag) {condition.await();}printBar.run();flag = true;condition.signal();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}}

Condition类:从整体上来看 Object 的 wait 和 notify/notifyAll 是与对象监视器 Synchronized 配合完成线程间的等待/通知机制,而Condition 的 await和 signal/signalAll 是与 Lock 配合完成等待通知机制。前者是java底层级别的,后者是语言级别的,两者用法基本类似,后者具有更高的可控制性和扩展性。
详细可参考:Condition类

五、CyclicBarrier 类

 volatile boolean flag = true;CyclicBarrier cyclicBarrier = new CyclicBarrier(2);public void foo(Runnable printFoo) throws InterruptedException {for (int i = 0; i < n; i++) {while (!flag);printFoo.run();flag = false;try {cyclicBarrier.await();}catch(BrokenBarrierException e) {e.printStackTrace();}}}public void bar(Runnable printBar) throws InterruptedException {for (int i = 0; i < n; i++) {try {cyclicBarrier.await();}catch(BrokenBarrierException e) {e.printStackTrace();}printBar.run();flag = true;}}

CyclicBarrier类:可以理解为构造方法创建n个线程,如果其中一个线程执行遇到await()则阻塞,等待其他线程执行也遇到await()阻塞;若当前线程为最后一个遇到await()阻塞的线程时,则唤醒全部线程执行并做下一次线程组任务。
详细可参考:CyclicBarrier

六、CountDownLatch 类

 volatile boolean flag = true;CountDownLatch countDownLatch = new CountDownLatch(2);public void foo(Runnable printFoo) throws InterruptedException {for (int i = 0; i < n; i++) {while (!flag) {countDownLatch.countDown();countDownLatch.await();}printFoo.run();flag = false;}}public void bar(Runnable printBar) throws InterruptedException {for (int i = 0; i < n; i++) {while (flag) {countDownLatch.countDown();countDownLatch.await();}printBar.run();flag = true;}}

CountDownLatch:是一个同步工具类,用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成一些任务,然后在CountDownLatch上等待的线程就可以恢复执行接下来的任务。
详细可参考:CountDownLatch 类

CyclicBarrierCountDownLatch对比:

  • CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法重置,可以使用多次,所以CyclicBarrier能够处理更为复杂的场景;
  • CyclicBarrier还提供了一些其他有用的方法,比如getNumberWaiting()方法可以获得CyclicBarrier阻塞的线程数量,isBroken()方法用来了解阻塞的线程是否被中断;
  • CountDownLatch允许一个或多个线程等待一组事件的产生,而CyclicBarrier用于等待其他线程运行到栅栏位置。

七、Semaphore类

 private Semaphore foo = new Semaphore(1);private Semaphore bar = new Semaphore(0);public void foo(Runnable printFoo) throws InterruptedException {for (int i = 0; i < n; i++) {foo.acquire();printFoo.run();bar.release();}}public void bar(Runnable printBar) throws InterruptedException {for (int i = 0; i < n; i++) {bar.acquire();printBar.run();foo.release();}}

Semaphore类:信号量控制,详情参考:Semaphore

循环打印FooBar相关推荐

  1. Leetcode1115交替打印FooBar

    交替打印FooBar 1. 题目描述: 我们提供一个类: class FooBar {public void foo() {for (int i = 0; i < n; i++) {print( ...

  2. java两个线程循环打印_java循环打印 多线程

    问题描述 Java多线程,循环打印"我是多线程"10次. 思路: 1 开启5个线程 "我" "是" "多" " ...

  3. python画图代码星星-Python利用for循环打印星号三角形的案例

    简单的for循环打印三角形 1,for循环方法实现星星三角 代码: for i in range(0,5): for j in range(i+1): if i == 4: print("* ...

  4. JUC并发编程九 并发架构--循环打印

    使用wait-notify方式实现循环打印 import lombok.extern.slf4j.Slf4j;@Slf4j(topic = "c.TestCycle") publi ...

  5. SMARTFORM 循环打印实现 (循环调用SMARTFORM)

    用SMARTFORM做打印已经很久了,功能也了解了不少,但是还是有很多功能有待去学习. SAP做的东西都太强大了,怎么学也学不完.最近在做FORM打印的时候,客户要求按供应 商分类打印,并且按每个供应 ...

  6. python循环五角星做法_python实现while循环打印星星的四种形状

    在控制台连续输出五行*,每一行星号数量一次递增 * ** *** **** ***** #1.定义一个行计数器 row = 1 while row <= 5: #定义一个列计数器 col = 1 ...

  7. 循环: 打印1~10

    /* 循环: 打印1~10 */ set serveroutput on declare   pnum NUMBER := 1; begin   loop     --退出条件     exit wh ...

  8. Python 中使用 for、while 循环打印杨辉三角练习(列表索引练习)。

    Python中使用for while循环打印杨辉三角练习(列表索引练习). 杨辉三角是一个由数字排列成的三角形数表,一般形式如下: 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 ...

  9. LeetCode 多线程 1115. 交替打印FooBar

    1115. 交替打印FooBar Ideas 交替锁的设计,两把锁,foo执行的时候把foo lock acquire,print完了只有把bar lock release,这样foo就得等着,然后b ...

最新文章

  1. oracle9i数据库自动备份,Windows环境下Oracle9i数据库文件的自动备份
  2. Python 工具链让你写的代码更规范
  3. FFmpeg AVCodecContext结构体debug变量剖析
  4. C代码中如何调用C++ C++中如何调用C
  5. spring EL 实现ref的效果
  6. Linux下编译运行Go程序
  7. php 中文拼音,php中文转拼音
  8. Hadoop源码分析16: IPC流程(11) 整体流程
  9. android web developer,Growth: 一个关于如何成为优秀Web Developer 的 App
  10. assertionerror python_Python 基础(十四): 错误和异常
  11. python画条形图-python使用Matplotlib画条形图
  12. 《人工智能:计算Agent基础》——3.3 图搜索
  13. python 参数一样结果不一样_优化Keras的超参数:相同参数的结果不同
  14. React使用ECharts
  15. Vue项目实战之电商后台管理系统(三) 用户管理模块
  16. 关于文件和文件指针的总结
  17. 登录到接收邮件服务器(pop3):验证失败,Office2010的outlook pop3邮箱设置问题
  18. Epoch Based Reclamation 的个人理解
  19. 云集宣布品牌升级,推出全新slogan“购物享受批发价”
  20. Gradle基础:4:Task的使用方式

热门文章

  1. layui 数据表格+分页
  2. 猿如意|IntelliJ IDEA Ultimate
  3. 计算机基础—任意整数补码的快速计算方法
  4. 100套炫酷网站错误页Html5模板(403,404,500等)
  5. JWT基本概念和使用介绍
  6. 勒索病毒WannaCry深度技术分析——详解传播、感染和危害细节
  7. ESP 保姆级教程 基础篇 —— 环境安装、NodeMcu引脚介绍
  8. 融云入选中国信通院《高质量数字化转型产品及服务全景图》
  9. Java之构造函数 翔细开VAP.
  10. linux下sigaction函数,sigaction函数解析