
毕业那天,你学的东西就被推翻了
最近鸭鸭在脉脉看到一条提问,看完后愣了一下:
“为什么大学禁止用 AI,而公司要求全用 AI?”

发帖的是一位前科大讯飞的工程师,问题特别简单,但底下 90 多条评论吵得很凶。鸭鸭把评论区里出现频率最高的几种说法归纳一下

有人上来就开喷:
“学校连个像样的 AI 课都没有,毕业了又骂学生不会用 AI,这套逻辑我服了。”
也有人替学校说话:
“不让用是为了你好。你连 print 都不会写,AI 替你写完,你以为你会了,其实啥也不会。”
还有最戳的一条:
“学校教你做人,公司教你做事。这两件事在 AI 这件事上,正好反过来了。”
最后这句话,鸭鸭看完之后特别想拍大腿。
而且这真不是个段子。鸭鸭翻了下这两个月的新闻:
去年底,复旦大学正式发布《关于在本科毕业论文(设计)中使用 AI 工具的规定》,明确六个禁止,情节严重的可以撤销学位(澎湃新闻有专访报道)。中国传媒大学、华北电力大学、湖北大学也跟着发了类似通知,学校这边,AI 几乎被视为作弊工具。
而企业那边,4 月初澎湃新闻一篇《大厂“牛马”,被迫用 AI》直接把另一面端出来:有人被统计每天烧多少 Token,有人公司发的 AI 额度用不完会被回收账号,有人 Leader 直接强制“所有产出都得先让 AI 生成一版”。前两周,老黄也在英伟达内部发话:全员必须用 Codex。
4 月份的应届生,刚交完论文证明这是自己一字一句写的,5 月初入职第一天,Leader 就问他:你 Cursor/Claude/Copliot 装了吗?
中间隔的,可能就一张机票的距离。
……
说实话,鸭鸭一开始也觉得这是教育落后于行业的老问题。但后来想想,没那么简单。
学校和公司在防的,根本不是同一个东西。
学校真正在防的,是身份冒名。老师布置一篇论文,本质上是在让你证明这些字、这些代码、这些推导是你脑子过过的,不是别人代笔的。AI 出现以前,老师防的是抄袭、防的是代写;AI 出现以后,老师防的是你把脑子外包给了模型。
所以学校的逻辑很清楚:考核的是原创性,AI 在这条线上等同于作弊。
公司真正怕的,是你产出跟不上。它不在乎那段代码是 Cursor 写的还是你手敲的,只看一件事,今天能不能上线、今晚能不能修 bug。AI 替你写了 80% 还能跑,老板高兴;你自己慢慢敲到深夜,老板下次绩效给你打 3.5。
你看,学校在按过程打分,公司在按产出算钱。两边的考核函数完全不一样,AI 的角色当然就反过来了。

更尴尬的是没人会告诉你这两套规则的差别。学校老师不会说你毕业后这套规则就作废了,公司 HR 也不会跟你解释为什么之前你被罚的事情现在变成了 KPI。你只能自己悄悄完成这次切换,并假装没发生过任何冲突。
那这种切换里,谁吃亏最大?
不是不会用 AI 的同学,他们工作两个月就能补上。
是那些把原创性当信仰的好学生。
那些在大学里坚持古法编程、每篇论文自己读,把原创性看得比交付速度更重要的同学,进了公司之后,会经历一段非常痛苦的认知重塑。因为公司不奖励原创,公司奖励交付。
鸭鸭自己刚毕业那会儿就是这样,特别看不上用工具糊弄的同事,结果半年后才发现,那些同事的产出比鸭鸭多三倍,绩效比鸭鸭高一档。
那这件事到底该怎么办?鸭鸭不打算给三条客套建议,就说三个真实的转变:
最后说一句鸭鸭自己的看法。
学校禁 AI 不一定错,公司逼 AI 也不一定对。真正错的,是没人帮应届生处理这中间的认知断层。
你能做的,就是自己提前两年开始切换。等别人还在为学术不端吓得不敢碰 AI 的时候,你已经在用 AI 帮自己刷题、模拟面试、写简历、改面经了。
毕业那天的差距,就是这两年攒出来的。
你们怎么看这事?大学和公司的 AI 规则,你觉得到底谁更对?评论区聊聊。
……
今天鸭鸭和大家分享一道 后端场景题面试题。
【编写一段代码,使得这段代码必定会产生死锁,不能使用Thread.sleep() 】
关键是要确保两个线程同时持有各自的锁,然后再去争抢对方的锁,这样就能稳定复现死锁。
用 CountDownLatch 就能做到。
核心思路是:

代码实现:
importjava.util.concurrent.CountDownLatch;
publicclassGuaranteedDeadlock {
privatestaticfinalObjectlock1=newObject();
privatestaticfinalObjectlock2=newObject();
privatestaticfinalCountDownLatchlatch=newCountDownLatch(2);
publicstaticvoidmain(String[] args) {
Threadthread1=newThread(() -> {
synchronized (lock1) {
System.out.println("Thread 1: Holding lock1...");
latch.countDown(); // 让 thread2 也开始执行
try { latch.await(); } catch (InterruptedExceptione) { e.printStackTrace(); } // 保证两个线程同时竞争
synchronized (lock2) {
System.out.println("Thread 1: Acquired lock2!");
}
}
});
Threadthread2=newThread(() -> {
synchronized (lock2) {
System.out.println("Thread 2: Holding lock2...");
latch.countDown(); // 让 thread1 也开始执行
try { latch.await(); } catch (InterruptedExceptione) { e.printStackTrace(); } // 保证两个线程同时竞争
synchronized (lock1) {
System.out.println("Thread 2: Acquired lock1!");
}
}
});
thread1.start();
thread2.start();
}
}除了 CountDownLatch,也可以用 CyclicBarrier 实现同样的效果。
很多人第一反应会写这样的代码:
publicclassDeadlockExample {
privatestaticfinalObjectlock1=newObject();
privatestaticfinalObjectlock2=newObject();
publicstaticvoidmain(String[] args) {
Threadthread1=newThread(() -> {
synchronized (lock1) {
System.out.println("Thread 1: Holding lock1...");
// 试图获取 lock2
synchronized (lock2) {
System.out.println("Thread 1: Acquired lock2!");
}
}
});
Threadthread2=newThread(() -> {
synchronized (lock2) {
System.out.println("Thread 2: Holding lock2...");
// 试图获取 lock1
synchronized (lock1) {
System.out.println("Thread 2: Acquired lock1!");
}
}
});
thread1.start();
thread2.start();
}
}上面这段代码看起来会死锁,实际上死锁发生的概率远不是 100%。
问题就出在线程调度的不确定性上。
Java 的线程调度由操作系统决定,执行顺序完全不可预测。
很可能线程 1 在线程 2 启动前就已经快速拿到了 lock1 和 lock2,执行完毕释放了锁,线程 2 再启动时不会遇到任何阻塞,压根就不会死锁。

想要稳定复现死锁,必须让两个线程先分别持有自己的锁,然后同时去争抢对方的锁。
CountDownLatch 的作用就是控制这个时机,让两个线程在各自持有锁之后互相等待,等双方都准备好了再同时往下执行,这样就能保证 100% 死锁。
CountDownLatch 是倒计时门闩,构造时传入一个计数值,每次调用 countDown() 减一,当计数归零时所有调用 await() 的线程同时被唤醒。特点是一次性使用,用完就废了。
CyclicBarrier 是循环栅栏,构造时传入一个参与线程数,每个线程调用 await() 后会阻塞,直到所有线程都到达栅栏位置,然后一起被释放。特点是可以复用,释放后会重置计数,可以继续使用。
对于制造死锁来说,两者都能用,但 CountDownLatch 更直观,因为它天然就是"等所有人准备好再一起开始"的语义。CyclicBarrier 也可以,但它的重置特性在这里用不上。
线上环境如果怀疑发生了死锁,可以用 jstack 或 jconsole 查看线程堆栈,JVM 会自动检测死锁并在堆栈信息里标注出来。
避免死锁的常见手段:
1)加锁顺序一致:所有线程按照相同的顺序获取多个锁,比如都先拿 lock1 再拿 lock2,这样就不会出现循环等待。
2)尝试加锁超时:用 ReentrantLock.tryLock(timeout) 替代 synchronized,拿不到锁就放弃已持有的锁,过一会重试。
3)减少锁粒度:不要一把大锁锁住整个方法,尽量缩小锁的范围,减少持有锁的时间。
4)避免嵌套加锁:如果业务允许,尽量不要在持有一把锁的情况下再去获取另一把锁。