算法笔记-lc-1114. 按序打印(多线程简单题)

【力扣分模块练习】并发/线程安全 1114. 按序打印 我们提供了一个类: public class Foo { public void first() { print(“first”); } public void second() { print(“second”); } public void third() { print(“third”); } } 三个不同的线程 A、B、C 将会共用一个 Foo 实例。 一个将会调用 first() 方法 一个将会调用 second() 方法 还有一个将会调用 third() 方法 请设计修改程 阅读详情

@[TOC](算法笔记-lc-1114. 按序打印(多线程简单题))

题目

题干

给你一个类:

public class Foo {
public void first() { print(“first”); }
public void second() { print(“second”); }
public void third() { print(“third”); }
}
三个不同的线程 A、B、C 将会共用一个 Foo 实例。

线程 A 将会调用 first() 方法
线程 B 将会调用 second() 方法
线程 C 将会调用 third() 方法
请设计修改程序,以确保 second() 方法在 first() 方法之后被执行,third() 方法在 second() 方法之后被执行。

提示:

尽管输入中的数字似乎暗示了顺序,但是我们并不保证线程在操作系统中的调度顺序。
你看到的输入格式主要是为了确保测试的全面性。

示例

示例 1:

输入:nums = [1,2,3]
输出:“firstsecondthird”
解释:
有三个线程会被异步启动。输入 [1,2,3] 表示线程 A 将会调用 first() 方法,线程 B 将会调用 second() 方法,线程 C 将会调用 third() 方法。正确的输出是 “firstsecondthird”。
示例 2:

输入:nums = [1,3,2]
输出:“firstsecondthird”
解释:
输入 [1,3,2] 表示线程 A 将会调用 first() 方法,线程 B 将会调用 third() 方法,线程 C 将会调用 second() 方法。正确的输出是 “firstsecondthird”。

提示:
nums 是 [1, 2, 3] 的一组排列

题解

并发问题

并发问题来自并发计算的场景,该场景下,程序在多线程(或多进程)中 同时 执行。

同时进行并不是完全指进程或线程在不同的物理 CPU 上独立运行,更多情况下,是在一个物理 CPU 上交替执行多个线程或进程。并发既可在线程中,也可在进程中。

并发主要为多任务情况设计。但如果应用不当,可能会引发一些漏洞。按照情况不同,可以分为三种:

竞态条件:由于多进程之间的竞争执行,导致程序未按照期望的顺序输出。

死锁:并发程序等待一些必要资源,导致没有程序可以执行。

资源不足:进程被永久剥夺了运行所需的资源。

此题中存在竞态条件。下面展示一个竞态条件的例子。

假设有一个方法 withdraw(amount),如果请求量小于当前余额,则从当前余额中减去请求量,然后返回余额。方法定义如下:

int balance = 500;
int withdraw(int amount) {
  if (amount < balance) {
    balance -= amount;
  }
  return balance;
}

我们 期望 该方法执行后余额永远不会为负。

但是有可能出现竞态条件,使得余额变为负数。假设两个线程同时使用不同的参数执行该方法。例如:线程 1 执行 withdraw(amount=400),线程 2 执行 withdraw(amount=200)。这两个线程的执行顺序如下图所示。在每个时刻只执行一条语句。

在这里插入图片描述

上述流程执行结束后,余额变成负数,这并不是期望的输出。

无竞争并发

并发问题有一个共同特征:多个线程/进程之间共享一些资源(例如:余额)。由于无法消除资源共享的约束,防止并发问题就变成了 资源共享的协调 问题。

根据这个思路,如果可以确保程序中 关键部分代码的独占性(例如:检查和减少余额),就可以防止程序进入不一致的状态。

竞争条件的解决方案为:需要某些关键部分代码具有排他性,即在给定的时间内,只有一个线程可以进入关键部分代码。

可以将这种机制看做限制关键部分代码访问的锁。在前面示例的关键部分代码加锁,即检查余额和减少余额的语句。然后重新运行两个线程,会有下图的执行顺序:

在这里插入图片描述

在该机制下,一旦一个线程进入关键部分,它就可以阻止其他线程进入该关键部分。例如,在时间点 3,线程 2 进入关键部分,那么在时间点 4,如果没有锁保护,线程 1 就可能进入关键部分。最后两个线程同时运行,保证系统的一致性,并确保余额正确。

如果该线程未被授权进入关键代码,可以认为该线程被阻塞或进入睡眠状态。例如,线程 1 在时间点 4 被阻塞,之后关键部分被释放,可以通知其他等待线程。线程 2 在时间点 5 释放了关键部分,就可以通知 线程 1 进入。

这种机制还具有唤醒其他等待线程的功能。

总之,为了防止出现并发竞争状态,需要一种具有两种功能的机制:1)关键部分的访问控制;2)通知阻塞线程。

方法一:使用 synchronization

思路

题目要求按顺序依次执行三个方法,且每个方法都在单独的线程中运行。为了保证线程的执行顺序,可以在方法之间创建一些依赖关系,即第二个方法必须在第一个方法之后执行,第三个方法必须在第二个方法之后执行。

方法对之间的依赖关系形成了所有方法的特定的执行顺序。例如 A < B, B < C,则所有方法的执行顺序为 A < B < C。

依赖关系可以通过并发机制实现。使用一个共享变量 firstJobDone 协调第一个方法与第二个方法的执行顺序,使用另一个共享变量 secondJobDone 协调第二个方法与第三个方法的执行顺序。

算法

首先初始化共享变量 firstJobDone 和 secondJobDone,初始值表示所有方法未执行。

方法 first() 没有依赖关系,可以直接执行。在方法最后更新变量 firstJobDone 表示该方法执行完成。

方法 second() 中,检查 firstJobDone 的状态。如果未更新则进入等待状态,否则执行方法 second()。在方法末尾,更新变量 secondJobDone 表示方法 second() 执行完成。

方法 third() 中,检查 secondJobDone 的状态。与方法 second() 类似,执行 third() 之前,需要先等待 secondJobDone 的状态。

实现

上述算法的实现在很大程度上取决于选择的编程语言。尽管在 Java,C++ 和 Python 中都存在互斥与信号量,但不同语言对并发机制有不同实现。

class Foo {

  private AtomicInteger firstJobDone = new AtomicInteger(0);
  private AtomicInteger secondJobDone = new AtomicInteger(0);

  public Foo() {}

  public void first(Runnable printFirst) throws InterruptedException {
    // printFirst.run() outputs "first".
    printFirst.run();
    // mark the first job as done, by increasing its count.
    firstJobDone.incrementAndGet();
  }

  public void second(Runnable printSecond) throws InterruptedException {
    while (firstJobDone.get() != 1) {
      // waiting for the first job to be done.
    }
    // printSecond.run() outputs "second".
    printSecond.run();
    // mark the second as done, by increasing its count.
    secondJobDone.incrementAndGet();
  }

  public void third(Runnable printThird) throws InterruptedException {
    while (secondJobDone.get() != 1) {
      // waiting for the second job to be done.
    }
    // printThird.run() outputs "third".
    printThird.run();
  }
}

方法二:信号量机制

private Semaphore two = new Semaphore(0);
    private Semaphore three = new Semaphore(0);

    public Foo() {

    }

    public void first(Runnable printFirst) throws InterruptedException {

        // printFirst.run() outputs "first". Do not change or remove this line.
        printFirst.run();
        two.release();
    }

    public void second(Runnable printSecond) throws InterruptedException {
        two.acquire();
        // printSecond.run() outputs "second". Do not change or remove this line.
        printSecond.run();
        three.release();
    }

    public void third(Runnable printThird) throws InterruptedException {
        three.acquire();
        // printThird.run() outputs "third". Do not change or remove this line.
        printThird.run();
    }

方法三:wait()+notifyAll()

class Foo {
    
    private boolean firstFinished;
    private boolean secondFinished;
    private Object lock = new Object();

    public Foo() {
        
    }

    public void first(Runnable printFirst) throws InterruptedException {
        
        synchronized (lock) {
            // printFirst.run() outputs "first". Do not change or remove this line.
            printFirst.run();
            firstFinished = true;
            lock.notifyAll(); 
        }
    }

    public void second(Runnable printSecond) throws InterruptedException {
        
        synchronized (lock) {
            while (!firstFinished) {
                lock.wait();
            }
        
            // printSecond.run() outputs "second". Do not change or remove this line.
            printSecond.run();
            secondFinished = true;
            lock.notifyAll();
        }
    }

    public void third(Runnable printThird) throws InterruptedException {
        
        synchronized (lock) {
           while (!secondFinished) {
                lock.wait();
            }

            // printThird.run() outputs "third". Do not change or remove this line.
            printThird.run();
        } 
    }
}

方法四:sleep(0)

class Foo {
    int flag=1;
    public Foo() {

    }

    public void first(Runnable printFirst) throws InterruptedException {
        // printFirst.run() outputs "first". Do not change or remove this line.
        printFirst.run();
        flag=2;
    }

    public void second(Runnable printSecond) throws InterruptedException {
        while(true){
            if(flag>=2){
                break;
            }
            else{
                Thread.sleep(0);
            }
        }
        // printSecond.run() outputs "second". Do not change or remove this line.
        printSecond.run();
        flag=3;
    }

    public void third(Runnable printThird) throws InterruptedException {
         while(true){
            if(flag>=3){
                break;
            }
            else{
                Thread.sleep(0);
            }
        }
        // printSecond.run() outputs "second". Do not change or remove this line.
        printThird.run();
    }
}
【信息科学与工程学】【通信工程】第二百四十篇 IPv4/IPv6 功能点、特性和算法及 IPv4→IPv6 迁移特性02 说明:按「纯数学 → 数学物理 → 多物理场 → 界面/表面科学耦合 + 数学化学 + 运动科学(0.01 nm → μm → mm → cm 精度及超长运动)」纵向穿透,每一行给出,最后挂。 阅读详情

相关推荐

【信息科学与工程学】信息科学领域——第一百三十五篇 射频/天线03

按照之前的模式,继续补充961-970,保持领域覆盖均衡,延续材料、几何、拓扑、电磁、组网、信号、脉冲、数据传输、信号采集、信号编解码十个方向,每个方向至少一题,可以适当穿插一些之前未充分覆盖的子领域(比如材料还可以做钙钛矿、MXene等;我们收到了用户要求继续补充的问题,需要新增编号,并关注射频/天线的材料领域、几何、拓扑、电磁、组网、信号、脉冲、数据传输、信号采集、信号编解码领域。注意保持表格格式一致,内容涵盖射频/天线领域的各种子领域,包括6G、5G、脉冲、波/函数、组合、组网、射频设计等。

weixin_49199313的博客 263

按序打印(多线程同步机制)

题目给出的初始代码: class Foo { public Foo() { } public void first(Runnable printFirst) throws InterruptedException { // printFirst.run() outputs "first". Do not change or remove this line. print...

qq_61009660的博客 398

参考线平滑-FemPosDeviation-SQP

使用SQP(顺序二次规划)对离散点进行平滑

mpt0816的博客 2439

Leet-code 按序打印问题(包含Semaphore工具类讲解)

一、原题展示: 二、解题思路: 三、题解代码:

qq_62678419的博客 264

山东大学软件测试实验2——系统功能测试

​ 给出一个被测试的 Web 应用系统(SUT)。http://211.87.232.162:8080/注:友情链接不用延伸测试。针对被测试的 Web 应用系统进行功能测试,发现其存在的缺陷。 ​

m0_74033391的博客 2409

YOLO11涨点优化:训练技巧 | 使用标签平滑(Label Smoothing)配合余弦退火学习率,防止过拟合,稳步提点

摘要: YOLO11凭借动态卷积核调度、自适应特征融合等创新成为2026年目标检测主流,但面临训练集过拟合问题。文章聚焦两种零推理开销的正则化技巧:标签平滑(Label Smoothing)和余弦退火学习率(Cosine Annealing LR)。标签平滑通过软化硬标签(如将[1,0]改为[0.9,0.1])防止模型过度自信,代码中通过label_smoothing=0.1一键启用;余弦退火则以平滑的余弦曲线调整学习率(cos_lr=True),避免阶梯衰减的震荡。两者组合可显著缩小YOLO11训练与验证

2601_95807009的博客 45

leetcode 打印_leetcode多线程之按序打印

序本文主要记录一下leetcode多线程之按序打印题目我们提供了一个类:public class Foo {public void first() { print("first"); }public void second() { print("second"); }public void third() { print("third"); }}三个不同的线程将会共用一个 Foo 实例。线程 A ...

weixin_34430580的博客 241

基于Cesium移动的天空云

天空中的云朵也算是一种天气气候的效果,它能更加逼真的反映当前的天气状况,可以是白云朵朵,也可以是乌云密布,结合其他天气效果,可以组合形成较为真实的三维可视化场景。

星云数海的博客,一个专注GIS十多年的GISer人。 447

原子操作类AtomicInteger详解

为什么需要AtomicInteger原子操作类? 对于Java中的运算操作,例如自增或自减,若没有进行额外的同步操作,在多线程环境下就是线程不安全的。num++解析为num=num+1,明显,这个操作不具备原子性,多线程并发共享这个变量时必然会出现问题。测试代码如下: public class AtomicIntegerTest { private static final int...

分享传递价值 22万+

FreeRTOS全攻略:从入门到精通

本文聚焦 FreeRTOS,从基础概念出发,阐述其开源、轻量级等优势。着重介绍 FreeRTOS 入门知识,涵盖内存管理、任务操作、同步互斥机制等核心内容。其中,空闲任务与钩子函数保障系统基础运行与功能扩展;队列、信号量、互斥锁和事件组等机制,分别从数据传递、资源访问控制、优先级问题解决及多任务同步等方面,为开发者提供高效的任务间协作与资源管理方案,助力开发者掌握 FreeRTOS 核心技术,为嵌入式项目开发筑牢基础。

邓邓子的博客 6358

2024年软考信息安全工程师备考历年真题汇总

信息安全工程师分属“信息系统”专业,位处中级资格,2016年下半年,第一次开考信息安全工程师(中级)考试。目前每年考试一次。已开考六次,2016年11月12日,2017年5月20日,2018年5月26日,2019年5月25日,2020年11月7日,2021年11月6日为第六次考试。 第五次和第六次考试,采用第二版教材。 2024年软考信息安全工程师备考历年真题汇总

月梦工作室 5104

[Java]LeetCode1114. 按序打印 | Print in Order

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/)➤GitHub地址:https://github.com/strengthen/LeetCode➤原文地址://www.cnblogs.com/streng...

weixin_30564785的博客 341

深度强化学习 | 基于SAC算法的移动机器人路径跟踪(附Pytorch实现)

软性演员-评论家(Soft Actor-Critic, SAC)算法是基于最大熵原理的离线策略方法,本节将基于深度强化学习中的SAC算法实现移动机器人的路径跟踪,介绍网络设计、动作空间设计和奖励函数设计,并提供Pytorch实现加深理解

FRIGIDWINTER的博客 5736

多线程之按顺序打印结果

题目 我们提供了一个类: public class Foo { public void one() { print("one"); } public void two() { print("two"); } public void three() { print("three"); } } 三个不同的线程将会共用一个 Foo 实例。 线程 A 将会调用 one() 方法 线程 B 将会调用 two() 方法 线程 C 将会调用 three() 方法 请设计修改程序,以确保 two() 方法在 one(

lingqu+wx bjmsb06 340

【蒸汽幻想台服新版商业端】2025最新整理单机一键即玩镜像端+WIN系半手工端+PC客户端+网页注册+网页管理后台+教程【站长亲测】

MMORPG端游【蒸汽幻想台服新版商业端】2025最新整理单机一键即玩镜像端+WIN系半手工端+PC客户端+网页注册+网页管理后台+教程。

百创科技 531

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

按序执行

题型:多线程 难度系数:简单 题目:有三个目标方法,用于打印出不同的结果。现提供三个线程,分别执行不同的方法,需按一定的顺序执行这三个方法。 示例: 1、 输入: [1,2,3] 输出: "onetwothree" 解释: 有三个线程会被异步启动。 输入 [1,2,3] 表示线程 A 将会调用 one() 方法,线程 B 将会调用 two() 方法,线程 C 将会调用 three() 方法。 正确的输出是 "onetwothree"。 2、 输入: [1,3,2] 输出: "onetwot.

weixin_44245778的博客 333

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

原子操作类AtomicInteger应用案例

有一个按序打印的需求: 我们提供了一个类: class Foo { public void first() { System.out.println("first"); } public void second() { System.out.println("second"); } public void third() { System.out.println("third"); } } 三个不同的线程将会共用一个 Foo 实例。 线程 A 将会调用 first() 方法 线程

李利科 374

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

java int 原子_Java原子操作AtomicInteger

一、前言JDK1.5之后的java.util.concurrent.atomic包里,多了一批原子处理类。AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference。主要用于在高并发环境下的高效程序处理,来帮助我们简化同步处理.二、AtomicIntegerAtomicInteger,一个提供原子操作的Integer的类。在Java语言中,++i和...

weixin_29062865的博客 455

(多线程)leetcode1114. 按序打印 认识AtomicInteger

我们提供了一个类: public class Foo { public void one() { print("one"); } public void two() { print("two"); } public void three() { print("three"); } } 三个不同的线程将会共用一个Foo实例。 线程 A 将会调用 one() 方法 线程 B 将会调...

少说,多做。 4443

AtomicInteger的分析使用

测试方法 private static AtomicInteger atomic = new AtomicInteger(0); private static int dex = 0; public static void main(String args[]){ testAtomic(); // testInt(); } privat...

奋斗 1180

AtomicInteger类的理解与使用

前言 JDK1.5之后的java.util.concurrent.atomic包里,多了一批原子处理类。AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference。主要用于在高并发环境下的高效程序处理,来帮助我们简化同步处理. AtomicInteger类的理解与使用 首先看两段代码,一段是Integer的,一段是AtomicInte...

萨达哈鲁的博客 775

并发编程——按顺序打印(AtomicInteger类)

并发编程——AtomicInteger类

雪人奥特曼的博客 371

深入理解AtomicInteger

AtomicInteger类是jdk并发包下的一个原子操作类,顾名思义原子操作类就是实现变量的原子操作的,要深刻理解这个类的作用我们要先从i++这个操作说起。 情景假设:商城后台需要录入库存,现在商品A剩下0个库存,仓库管理员A点击添加按钮添加一个库存,仓库管理员B点击添加按钮添加一个库存。代码如下: public class Product{ private int i; Pro

悟 680

按序打印

欢迎访问我的blog http://www.codinglemon.cn/ 好久没有写leetCode的算法题了,先写一个简单的试试水。 1114. 按序打印 1. 题目描述 我们提供了一个类: public class Foo { public void first() { print("first"); } public void second() { print("second"); } public void third() { print("third"); } } 三个不同的线.

zry15671554200的博客 256

视频隐私脱敏工程化:CSRT/KCF追踪算法在本地推理中的实现与边界

在短视频内容合规审查中,动态遮挡(Dynamic Masking)是高频且高耗时的环节。传统手动关键帧打码不仅效率低下,且在目标快速移动或遮挡时极易出现遮罩漂移。本文从计算机视觉工程角度,解析基于CSRT与KCF算法的目标追踪方案在视频隐私处理中的技术路径、性能权衡及落地实践。

Mysssone的博客 231

【算法拾光】链表 哈希表|K个一组翻转链表 + 两数之和

K个一组翻转链表需分段反转,借助哑节点与前驱指针维护连接,避免断链;两数之和利用哈希表实现O(1)查找,空间换时间,高效定位目标组合。前者考察链表指针操作,后者聚焦映射关系构建,均为面试高频题。

wuqingyigexiaoba的博客 317
上一篇: Redis中的红锁
下一篇: Protocol Buffer详解
Hell_potato777
博客等级 码龄6年 47粉丝 106原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值