zoukankan      html  css  js  c++  java
  • LeetCode:交替打印【1115】

    LeetCode:交替打印【1115】

    题目描述

    我们提供一个类:

    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 实例。其中一个线程将会调用 foo() 方法,另一个线程将会调用 bar() 方法。

    请设计修改程序,以确保 "foobar" 被输出 n 次。

    示例 1:

    输入: n = 1
    输出: "foobar"
    解释: 这里有两个线程被异步启动。其中一个调用 foo() 方法, 另一个调用 bar() 方法,"foobar" 将被输出一次。

    示例 2:

    输入: n = 2
    输出: "foobarfoobar"
    解释: "foobar" 将被输出两次。

    题目分析

      在解决1114问题按序打印时,我们曾引入了一个Java多线程工具类倒计时器(CountDownLatch),这里要介绍另外一个工具类信号量(Semaphore)。了解更多

      信号量(Semaphore)由一个值和一个指针组成,指针指向等待该信号量的进程。信号量的值表示相应资源的使用情况。信号量S≥0时,S表示可用资源的数量。

      信号量可以被两个操作修改:

    • 执行一次P操作意味着请求分配一个资源,因此S的值减1;当S<0时,表示已经没有可用资源,S的绝对值表示当前等待该资源的进程数。请求者必须等待其他进程释放该类资源,才能继续运行。
    • 执行一次V操作意味着释放一个资源,因此S的值加1;若S<0,表示有某些进程正在等待该资源,因此要唤醒一个等待状态的进程,使之运行下去。
    注意:信号量的值只能由PV操作来改变。

      我们可利用信号量来描述程序或语句之间的前趋关系

      设有两个并发执行的进程 P1 和 P2。 P1 中有语句 S1;P2 中有语句 S2。我们希望在 S1 执行后再执行 S2。为实现这种前趋关系, 我们只须使进程 P1 和 P2 共享一个公用信号量 S,并赋予其初值为 0,将 signal(S)操作放在 语句 S1 后面;而在 S2 语句前面插入 wait(S)操作,即

    • 在进程 P1 中,用 S1;signal(S);
    • 在进程 P2 中,用 wait(S);S2;

      由于 S 被初始化为 0,这样,若 P2 先执行必定阻塞,只有在进程 P1 执行完 S1;signal(S);操作后使 S 增为 1 时, P2 进程方能执行语句 S2 成功

      回到题中,先Foo后Bar,添加信号量S2(值为1),先申请资源后再输出Foo,此时S2为0,循环暂停,暂停前需要让Bar有机会输出,所以添加信号量S1(值为0),每当Foo输出后,都释放资源,这样Bar就可以输出了。

     

    Java题解

    import java.util.concurrent.Semaphore;
    
    class FooBar {
        private Semaphore s1 = new Semaphore(0);
        private Semaphore s2 = new Semaphore(1);
        private int n;
    
        public FooBar(int n) {
            this.n = n;
        }
    
        public void foo(Runnable printFoo) throws InterruptedException {
            
            for (int i = 0; i < n; i++) {
            	// printFoo.run() outputs "foo". Do not change or remove this line.
                s2.acquire();
                printFoo.run();
                s1.release();
            }
        }
    
        public void bar(Runnable printBar) throws InterruptedException {
            
            for (int i = 0; i < n; i++) {
                // printBar.run() outputs "bar". Do not change or remove this line.
                s1.acquire();
            	printBar.run();
                s2.release();
            }
        }
    }
    

      

     

  • 相关阅读:
    form表单重置、清空方法记录
    window location assign的使用
    简单易用的拾色器推荐
    display属性常用的四个值:block、inline、inline-block、none
    idea如何配置外部电脑访问本地项目
    Unity 2D相机公式换算(从其他博客上抄的)
    关于游戏逻辑模式的观点----谁调用谁
    Unity 角色场景传送功能
    Unity关于方法事件生命周期官方文档
    关于Unity物理事件的执行顺序的最新理解
  • 原文地址:https://www.cnblogs.com/MrSaver/p/11270859.html
Copyright © 2011-2022 走看看