渐渐能理解为何不愿意雇佣35岁以上程序猿。去年换了份工作,组里4位组员其中3位40+,发现其实最大的问题并不是说精力不济卷不动
刚看到个贴子,楼主说去年跳槽,新组4个人有3个40+,这才慢慢理解为什么很多公司不愿意招35+程序员。
贴子里大概意思是:这些前辈也不至于躺平,就是明显对新东西兴致一般,更在意稳定、下班时间和家庭节奏。网友们的回复我看了看,一边骂老板年龄歧视,一边又吐槽“老油条混日子”,两边都挺极端的。
我觉得这事吧,核心还真不在年龄,而在“心气”和“更新速度”。有的人30出头就开始摆烂,有的人40+还在啃英文文档、折腾新框架。企业算的是性价比:你要价高,就得么能扛事、么能带人、么能搞定复杂业务,不能只守着十年前那点老经验。
面试题:红绿灯路口
昨天晚上下班开车回家,在小区门口那个十字路口,又红灯 90 秒,绿灯 30 秒,我在那儿干瞪眼,就开始瞎想:要是这玩意儿让我用 Java 写个“红绿灯路口算法”,得怎么设计,程序才不撞车、不卡死、还好扩展。
你先想象这么个场景啊: 一个标准十字路口,只有两种车流方向:
南北直行一波 东西直行一波
先别管左转右转和行人,面试一般也不会一上来就要你全搞定。
我们把每辆车当成一个线程,当车到路口的时候,会调一个方法 carArrive,里边要完成两件事:
看看自己这条路现在是不是绿灯 是绿灯就过,不是就老老实实在那儿等
但是注意,有两个坑:
不能让南北和东西同时通行,不然就对撞了 不能让某个方向永远等不到绿灯,不然就“饿死”了
所以核心问题,其实就是“怎么用多线程同步原语,安全地切换红绿灯”。
我一般会这么干:把路口抽象成一个 TrafficLight 类,里面只有一把锁 + 当前绿灯方向 + 两个条件队列(南北一队,东西一队)。
先来个小枚举,别整一堆魔法字符串:
enum Direction {
NORTH_SOUTH,
EAST_WEST
}
路口的核心类长这样:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
publicclassTrafficLight{
privatefinal ReentrantLock lock = new ReentrantLock(true); // 公平一点
// 当前是哪个方向绿灯,默认先让南北走
private Direction green = Direction.NORTH_SOUTH;
privatefinal Condition nsCond = lock.newCondition();
privatefinal Condition ewCond = lock.newCondition();
// 车来了就调这个
publicvoidcarArrive(Direction dir, Runnable cross)throws InterruptedException {
lock.lock();
try {
// 不是自己方向的绿灯,就等
while (dir != green) {
if (dir == Direction.NORTH_SOUTH) {
nsCond.await();
} else {
ewCond.await();
}
}
// 真正过路的动作交给调用方
cross.run();
} finally {
lock.unlock();
}
}
// 红绿灯时间到了,定时调这个方法切换方向
publicvoidswitchLight(){
lock.lock();
try {
green = (green == Direction.NORTH_SOUTH)
? Direction.EAST_WEST
: Direction.NORTH_SOUTH;
// 切给谁,就把谁那一队全唤醒
if (green == Direction.NORTH_SOUTH) {
nsCond.signalAll();
} else {
ewCond.signalAll();
}
} finally {
lock.unlock();
}
}
}
上面这个逻辑你脑补一下现场:
每辆车就是一个线程,来了就 carArrive(direction, () -> print("我通过了"))如果刚好是自己的绿灯,直接 run,咻一下就过去了 如果不是,就在对应的 Condition上await()挂着休眠,不占 CPU外面再开一个“红绿灯控制器”线程,每隔 30 秒调一次 switchLight(),轮流放行两个方向
控制器大概长这样:
publicclassLightControllerimplementsRunnable{
privatefinal TrafficLight trafficLight;
privatefinallong greenDurationMillis;
publicLightController(TrafficLight trafficLight, long greenDurationMillis){
this.trafficLight = trafficLight;
this.greenDurationMillis = greenDurationMillis;
}
@Override
publicvoidrun(){
try {
while (true) {
Thread.sleep(greenDurationMillis);
trafficLight.switchLight();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
主程序一启动:
publicclassMain{
publicstaticvoidmain(String[] args){
TrafficLight trafficLight = new TrafficLight();
new Thread(new LightController(trafficLight, 30_000)).start();
// 模拟一堆车乱七八糟地来
for (int i = 0; i < 100; i++) {
Direction dir = (i % 2 == 0)
? Direction.NORTH_SOUTH
: Direction.EAST_WEST;
int carId = i;
new Thread(() -> {
try {
trafficLight.carArrive(dir, () ->
System.out.println("car " + carId + " " + dir + " pass"));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
}
}
这个版本有几个点是面试官爱追问的:
安全性:同一时刻只会有一个方向被放行,因为 green在锁保护下修改,while (dir != green)保证了只有绿灯方向能往下执行性能:没用 synchronized轮询那种土办法,而是Condition.await(),线程乖乖睡觉,唤醒才干活公平性:用了 ReentrantLock(true),虽然不能保证绝对平均,但至少不至于长时间饿死一边
顺便有一次我们线上做数据库压测,看过一份报告里专门对高并发更新做过对比,说 Postgres 在热点行更新场景里能把 MySQL 打出一个量级,这种“热点+排队”的问题本质上跟路口很像,只不过那边排的是行锁,这边排的是车
真要往现实靠,东西多得一塌糊涂:左转右转、掉头、行人、应急车、公交优先、感应式红绿灯……不过面试里你可以顺嘴说说自己的思路,显得脑子还在转:
左转/直行/右转怎么建模: 可以把“一个方向的一种动作”当成一个节点,比如“北向直行”“东向左转”,然后事先画一张“冲突表”:哪些动作可以同时放行,哪些必须互斥。算法层面就是一个冲突矩阵
conflict[i][j]。应急车优先: 可以给每个方向挂一个优先级队列,队头是救护车、消防车这类高优先级车,绿灯切换时先放这几辆,再放普通车。
智能一点的红灯时间: 现在我们是死板的 30 秒一切,其实完全可以根据等待队列长度动态调整,比如南北方向排了 50 辆车,东西才 3 辆,那下一轮就多给南北一点时间。甚至可以加个上限,保证东西不会一直饿着。
这些东西不一定真要你写代码,但你能把这个路口从一个 if-else 想到“冲突图 + 调度策略”,面试官一般就知道:你不仅会写锁,还能从系统角度去想资源调度。
行了,我这会儿再不睡明天早会就要趴着写代码了,你要真把上面这套敲一遍跑跑,多线程那块心里就稳很多了。
-END-