程序员老鬼

年底裁员新套路,leader找下属说有裁员名额,让你选择自己走,过几天再说裁员名额取消了,一分赔偿不用给!

刚看到个贴子,说年底裁员新套路:leader单独找人谈话,说现在有裁员名额,你要不要自己走,暗示还能体面点。结果人家开始准备交接了,过几天领导一句“名额取消了”,当事人成了主动想走的人,一分赔偿都没有。
我觉得这事吧,恶心就恶心在:公司想省钱可以理解,但别拿人当猴耍。说到底,这是典型的信息差+话术碾压,专挑不懂规则、又怕事的员工下手。换个角度想,真有名额,为什么不书面通知、走正规流程? 从我的角度看,遇到这种情况,记住两点:别口头答应“主动离职”,所有沟通尽量留痕;凡是让你签字的东西,拍照留底、看不懂就别签。

面 试 题 :H2O 生成

昨天晚上十一点多,我已经准备关电脑了,我们组那个小李突然甩给我一句:哥,你给我讲讲那个 H2O 生成的并发题呗,Java 的,我脑袋都要烧了。 我看了下题目,哎,这不是面试高频那个嘛。 简单说下题目哈:系统里会有很多线程在跑,氢原子线程会调用 hydrogen(Runnable releaseHydrogen) ,氧原子线程会调用 oxygen(Runnable releaseOxygen) ,这俩方法被乱七八糟地并发调用。你的任务就是——不管线程怎么来,你最后打印出来的字符串,每 3 个字符必须刚好组成一分子水:两个 H 一个 O ,顺序随意,比如 HHO 、 HOH 都行,但绝对不能出现 HHH 或 OOH 这种妖魔鬼怪。 小李一开始的思路特别典型,直接一个大锁 synchronized 套住,说我保证串行不就完事了吗。这个当然能跑,但是,面试官一看:好家伙,多线程题给你变成单线程题了,那问个啥劲儿,对吧。 我就跟他说,你别先想代码,先脑补一个生活画面:想象有一个小房间,每次最多只能进 3 个人,规定是两位“氢同学”和一位“氧同学”坐满才开饭。那我们需要解决两个小事: 一个是“座位怎么限制”,另一个是“凑够三个人再一起开饭”。 座位这个事,用 Java 里 Semaphore 特别顺手。氢有 2 个名额,氧有 1 个名额:
  • 对氢线程:想进来就 acquire() 一下氢的座位,没票就乖乖在门口等。
  • 对氧线程:同理,抢氧的那个座位。
这样一来,从入口就保证了“至多两个 H,一个 O”能进入一轮。 那“凑够三个人再一起开饭”呢,就像食堂阿姨喊:人满才打菜。这里可以用 CyclicBarrier(3) ,意思是:等到 3 个线程都调用了 await() ,大家再一起往下执行。它还能自动“重置”,下一拨 3 个再来一轮,正好对应一分子水。 所以整体套路就出来了:
  1. 氢线程先抢 hSemaphore 的票,再去 barrier.await() 集合,等人齐;放行后打印 H ,最后把票还回去。
  2. 氧线程抢 oSemaphore 的票,同样去 barrier.await() ,放行后打印 O ,再还票。
这样每一轮一定是 2 个氢线程 + 1 个氧线程站在 barrier 门口,凑够 3 个才一起往下走,打印完了把票还回去,下一轮继续抢座位。 给你看下完整代码,Java 版本的,面试直接抄脑子里这套就行了:
import java.util.concurrent.Semaphore;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.BrokenBarrierException;

publicclass H2O {

    // 氢最多允许两个线程同时参与一轮
    privatefinal Semaphore hSemaphore = new Semaphore(2);
    // 氧一轮只能来一个
    privatefinal Semaphore oSemaphore = new Semaphore(1);
    // 每 3 个线程组成一分子水
    privatefinal CyclicBarrier barrier = new CyclicBarrier(3);

    public H2O() {
    }

    public void hydrogen(Runnable releaseHydrogen) throws InterruptedException {
        // 抢氢的名额,抢不到就等下一轮
        hSemaphore.acquire();
        try {
            // 等这一轮的 3 个线程都到齐
            barrier.await();
        } catch (BrokenBarrierException e) {
            // 简单处理下,真实项目里可以打日志之类
            thrownew RuntimeException(e);
        }
        // 真正打印一个 H
        releaseHydrogen.run();
        // 把名额还回去,给下一轮用
        hSemaphore.release();
    }

    public void oxygen(Runnable releaseOxygen) throws InterruptedException {
        // 抢氧的那个唯一名额
        oSemaphore.acquire();
        try {
            barrier.await();
        } catch (BrokenBarrierException e) {
            thrownew RuntimeException(e);
        }
        // 打印一个 O
        releaseOxygen.run();
        oSemaphore.release();
    }
}
LeetCode 上的判题器会传进来 releaseHydrogen 和 releaseOxygen ,里头一般就是 System.out.print("H") 或 System.out.print("O") ,所以你只负责把线程节奏控好就行。 这个写法有几个细节,小李当时听完是“哦哦哦”那种恍然大悟的表情:
  • 为啥要 barrier.await() 在前, run() 在后?因为 barrier 负责把这一轮的 3 个线程“锁在同一波”,大家同时往下跑,打印的顺序虽说可能有点乱(比如 O 先打出来也行),但至少保证每一波都是 3 个字符,而且由 2H+1O 组成。
  • 为啥 release() 要放在 run() 之后?否则你还没打印完,就提前把票发给下一批线程,理论上也能对,但容易把线程节奏搞得更晃悠,调试的时候你自己会被绕晕。
我顺便跟小李吐槽了一句:这题其实跟之前排查 TCP 包 1024 那个奇怪 Bug 的思路挺像的,都是先想“协议”和“边界”,再去看代码里怎么一步步保证,不然全是线程和字节流,脑子很快就糊成一团浆糊。 如果面试官再追问一句:“那我要生成 H3O 呢,或者别的比例呢?”你就顺着说思路: Semaphore 的初始许可数和 CyclicBarrier 的人数都可以跟着改,这套模型是通用的,只是现在这道题固定是 2:1,所以代码看起来比较“写死”。 行,我就先说到这儿,刚好咖啡也凉了,我去续一杯,你自己把代码敲一遍,别光截图收藏。 -END -
我为大家打造了一份RPA教程, 完全免费: songshuhezi.com/rpa.html 最后给大家分享一份不错的副业资料,点击下方公众号,回复关键字: 副业 领,也可以链 接我微信: hls404