粉丝1.9万获赞3.8万

有 a、 b、 c、 d 四个县城,如何让他们顺序执行四个县城的顺序之行?为啥不做成单县城呢?非要四个县城。那我换个问法,现在有一个轿车的业务流程,它包含了四个县城, 县城 a 它是检查用户的定位是否有效,县城 b 是 匹配附近的司机,县城 c 它是发送这个订单请求给司机,县城 d 是 用来更新客户端,已经匹配到司机。 如果说这四个县城我们没有给他编排成一个顺序的话,那么就可能会造成县城 b 先匹配到司机,但是发现县城 a 的 定位是无效的,那司机可能就会收到无效订单 啊。如果县城 d 先更新订单了,但是县城 c 没有找到司机,那用户就会看到已匹配到的这种状态,但实际上是没有人接单的。 但是如果我们把这四个业务步骤做成单线,那可能又会导致这个轿车的这个业务不能并发处理,用户只能一个一个的完成。轿车,我们把这四个业务步骤分别做成四个线程,并且编排成顺序串行之行,那就能保证局部串行而整体并发。 好的明白了,那这四个县城如何编排才能实现顺序执行呢?可以用这个专用方法,然后实现这个县城的插队的方式,实现县城的顺序执行。可以的,但是就用这种方法,它会造成主县城堵塞,性能不太好。类似这样的县城编排问题的话,实际上我们应该考虑 count launch complete table future 或者是县城池。

明明加了锁,数据还是乱了?并发编程里,县城修改了数据,其他县城就看不到代码,明明按顺序写的,执行起来却乱套了。大家好,我是韩明俊博。今天咱们深入聊聊并发编程的三大问题。 可见性、原则性、有序性,这三个才是并发 bug 的 根源。并发编程为什么这么难呢?它的根本原因有三个,第一个是可见性问题, 一个县城修改的数据,其他县城看不到,导致数据不一致。第二个是原子性问题,操作不是原子的,可能被打断,导致数据错乱。第三个是有序性问题, 指令重排序,执行顺序和代码不同,导致逻辑异常。这三个问题是并发编程的核心难点,必须理解。什么是可见性呢?可见性是指一个县城修改了共享变量的值,其他县城能立即看到修改后的值。 我们来看一个典型的场景,屏幕上有一个 visibility demo, 包含一个布尔类型的共享变量 flag 现成一执行 writer 方法,将 flag 设置为处。现成二执行 reader 方法,不断检查 flag 是 否为处。但是呢,现成二可能永远也看不到 flag, 等于 处,一直在循环。为什么会出现这个问题呢?根本的原因就是 cpu 缓存的架构,从主内存到 cpu 寄存器, 中间有多层缓存 l 三缓存 l 二缓存 l 一 缓存,最后到寄存器。问题就是这样产生的,第一步,县城一修改了 flag, 写入 cpu 一 的缓存还没刷新到主内存。第二步,县城二从 cpu 二的缓存读取 flag, 读到的是就职 false。 第三步,导致县城二永远看不到 flag 等于处,就好比两个人各看各的小抄, 你改了你的,我还在看我的旧版本。怎么解决可见性问题呢?第一个方案就是 volatile, 关键字在 flag 上面加上 volatile 就 能保证可见性。它的原理是写 volatile 变量时,强制刷新到主内存, 读 volatile 变量的时候,强制从主内存提取,禁止 cpu 缓存。第二个方案是 synchronize 的, 在 writer 方法上面加上 synchronize 的 退出同步块的时候,会强制刷新到主内存,保证可见性。第三个方案是 lock 锁,使用 lock 点 lock 加锁,释放锁时,刷新到主内存。 接下来看第二个问题,原子性。什么是原子性呢?一个操作或者是多个操作,要么全部执行,要么全部不执行,中间不可能被打 断。我们来看一个典型的案例,屏幕上的 atomic demo 包含一个整数变量, count increment 方法,执行 count 加加操作 main 方法创建两个县城, 每个县城执行一千次 increment。 那 按照道理来讲,最后 count 加加应该等于两千,数据错乱了,为什么 count 加加不是原子的呢? count 加加看似是一行代码, 实际包含三个步骤,第一步,读取 count 的 值到计算器,这是 log。 第二步,对值进行加一操作,这是 add。 第三步,将结果写回 count, 这是 store。 我 们看并发执行的时序,时刻 t 一 现成一,执行 log, 读取 count 等于零,时刻 t 二现成一,执行 add。 同时现成二也执行 log, 读到 count 等 于零,时刻 t 三现成一,执行 store。 count 等于一,现成二,执行 add。 时刻 t 四现成二,执行 store。 count 等于 一。两个县城都执行了 count 加加,但是 count 最终只是一,不是二,这就是原则性的问题。怎么解决原则性问题呢?方案一是 synchronize 的 关键字,在 increment 方法上面加上 synchronize 的 同一时刻,只有一个县城可以执行。 第二个方案是 raychain lock, 创建一个 lock 对 象,在 increment 方法中加锁 lock 点 lock 加锁,然后执行 count 加加,最后 lock 点按 lock 释放锁。第三个方案是原子类,也是最推荐将 count, 将 count 改成 atomic integer, 调用 increment and get 方法,它的底层是使用 cas 保证原子性性能更 高。接下来看看第三个问题,有序性?什么是有序性呢?程序执行的顺序和代码书写的顺序一致,我们来看看案例。 reorder demo 包含两个变量, a 和 flag 先乘以执行 writer 方法,先将 a 赋值为一,然 然后将 flag 设置为处,现乘二执行 rate 的 方法,如果 flag 为处,那就计算 a 乘以 a。 按照道理来讲,当 flag 为处的时候, a 应该等于一,但是实际上 a 可能还是零,因为步骤一和步骤二的执行顺序可能被交换。为什么会出现指令重排序呢?指令重排序发生在三个层面, 第一个是变应器优化重排序,变应器在不改变单线成羽翼的情况下调整指令顺序。第二个是指令并行重排序, cpu 将指令并行执行改变执行顺序。第三个是内存系统重排序, cpu 缓存和读写缓冲区使指令执行顺序和代码不同。这里有一个 s 杠, f 杠 serial 羽翼。 无论怎么重排序,单县城的执行结果不能改变,但是在多县城下重排序就可能导致问题。怎么解决有序性的问题呢?第一个方案是 volatile 关键字禁止重排序,在 flag 前面加上 volatile 就 能禁止重排序。原理就是 volatile 写之前的操作不会重排序到 volatile 写之后, volatile 读之后的操作不会 重排到 volatile 读之前。第二个方案是 synchronize 的, 在 writer 方法上面加上 synchronize 的 同步块内禁止重 排序。第三个方案是利用 g m m 的 happens 杠 b four 规则保证有序性。我们来对比一下三大问题,可见性问题,根本原因是 cpu 缓存不一致,典型的场景是一个县城修改,其他县城看不见。解决方案就是 volatile synchronized lock。 原子性问题,根本原因是操作被打断,典型的场景是 count 加加等复合操作,解决的方案是 synchronized lock。 原子类有序性问题的根本原因是指令重排序, 典型的场景是 dcl 单立 volatile 场景,解决方案就是 volatile synchronized。 这里有一个关联性需要注意, volatile 只能保证可见性和有序性,不能保证原子性。 synchronized 的 可以保证三者, lock 也可以保证三者。我们来看一个经典的实战案例,双重检查锁单立模式,简称 dcl, 屏幕上是错误版本。 get instance 方法中第一次检查 instance 是 否为空,如果为空,枷锁再次检查如果还是空,创建 single 的 对象,那问题在哪呢? new singleton 包含三个步骤,第一步是分配内存,第二步是出场对象,第三步是将 instance 指向内存,这三步有可能被重排序,它正确的版本是什么样的呢?在 instance 前面加上 volatile 禁止指定重排序, 这样就能保证 new singleton 的 三步操作不被打乱。这个实现有三个关键点,第一是 volatile 禁止指定重排序,它保证了 new singleton 的 三步操作按照顺序执行。第二, synchronize 的 保证原则性同意时刻只有一个县城执行。 第三,双重检查,提高性能,减少锁竞争。这个案例就完美的全释了并发症三大问题的综合应用。最后我们来回顾一下本期的核心要点,第一,可见性问题,它是由 c p u 缓存导致的,使用 volatile 或者是 synchronized 的 解决。第二,原子性问题, 操作被打断导致的,用锁或者是原子类解决。第三,有序性问题,指定充排序导致使用 volatile 或者是 synchronized 的 解。 第四,记忆口诀,可见性看得见, cpu 缓存别捣乱。原子性不能断,操作要么全,要么完,有序性不能乱,指令重排要禁止。下一期我们将深入探讨县城池的核心参数和执行原理。七大参数是什么? 县城池是如何工作的?拒绝策略有哪些?你在病发编程中遇到过哪些诡异的 bug? 是 可见性、原子性还是有序性的问题呢?欢迎评论区分享,我是寒冰巨魔,我们下期再见!

国产 gpu 龙头这波操作直接让市场炸锅了!上市才刚一周,摩尔县成就抛出个大公告,你用不超过七十五亿闲置募集资金搞现金管理!这消息一出,好多人蒙了, 钢布的钱不搞研发拿去理财?今天公司火速回应,咱用大白话好好唠唠到底咋回事,先给大家捋捋前因后果。十二月五号,摩尔县城才在科创板上市, 作为国产 gpu 第一股,上市当天就高开百分之四百六十八,总市值飙到三千多亿,风头无两。这次 i p o 他 募集了八十亿,扣除费用后,实际到手七十五点七六亿,本来计划全投去搞 ai 芯片、图形芯片研发,还有补充流动资金, 结果才过一周就公告要拿最多七十五亿闲置资金做现金管理,这比例快占到募集资金的百分之九十九了,难怪大家会疑惑。 不过别慌,公司今天直接回应了,核心就一句话,七十五亿是上限,不是真要花这么多,实际用的钱会明显少很多。而且这钱是闲置的木头项目是分阶段花钱的,不是一到账就全砸出去。 比如建实验室、买设备、招研发团队都有节奏,暂时用不上的钱,闲着也是闲着,就拿去买保本的理财大额存单,这些安全产品,既能赚点利息,又不影响后续研发用钱。可能有人要问了,好好的科技公司咋还搞起理财了? 其实这代 a 股太常见了,好多公司 ipo 募资后,项目推进有周期,钱不可能瞬间花完,闲置资金存银行利息太低,所以都会合规搞点现金管理,本质是让钱不闲着,提高资金使用效率,赚的收益还能反过来补贴研发、补充流动资金,对公司和股东都是好事儿。 而且监管管得很严,这钱只能买保本流动性好的产品,不能炒股,不能制压,也不能变相改用途,专款专用的底线不会破。 咱再说说摩尔县城这波操作的底气,它可是实打实的硬科技公司,成立五年就敢对标英伟达自主研发的 m u s a 架构,打破了国外垄断, 二零二五年上半年营收就超七亿,比过去三年总和还多。更关键的是,他对研发是真舍得砸钱,过去三年研发投入快四十亿,占营收比例超百分之六百。 这次募资的钱本来就明确要投去三个芯片研发项目,公司回应也说了,会持续加大研发投入,绝不会因为现金管理耽误核心技术攻坚。 不过咱也得客观看,这事也反映了硬科技研发的现实,芯片研发是个慢功夫,周期长,花钱有节奏,不是急吼吼把钱砸出去就行。 摩尔县城预计二零二七年才能盈利,现在手里握着充足现金,一边按节奏搞研发,一边让闲置资金增值,其实是挺稳妥的财务操作。 但后续也得关注两点,一是木头项目的推进进度,二是现金管理的实际规模。只要这两点不跑偏,就没啥大问题。 往深了想,这事也给咱科普了一个知识点,以后再看到上市公司公告闲置资金现金管理,别第一反应就觉得是不务正业。关键看三点,钱是不是募集资金,有没有明确的木头计划理财是不是合规保本? 像摩尔县城这样有清晰的研发目标,严格的资金监管,这种现金管理就是正常的财务精细化操作,反而能看出公司对资金的把控很稳妥。总结下来就是,摩尔县城这波七十五亿现金管理,不是不搞研发去理财,而是让闲置资金不浪费的合规操作, 既不影响芯片研发的核心任务,又能提高资金效率。对硬科技公司来说,这种稳扎稳打的财务策略反而更让人放心。毕竟搞芯片空间需要长期投入,手里有充足且高效利用的资金,才能在自主可控的赛道上走得更远。