毕业那天,你学的东西就被推翻了

最近鸭鸭在脉脉看到一条提问,看完后愣了一下:

“为什么大学禁止用 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 给你讲知识点、出测试题、Review 你写的代码,但别让它替你写论文。这样你毕业不会挂,工作之后也能马上切换。

  • 找工作面试前,准备好怎么讲清楚自己是如何用 AI 的。不要只说会用 Cursor,要能说清哪一步让 AI 做、哪一步自己做、为什么这么分。这是 2026 年应届生面试的隐藏关卡。

  • 入职后第一周,主动去问老员工团队的 AI 工具栈是什么。不要等 leader 来安排,AI 工具的使用习惯属于看不见的入职门槛,越早上手,越早摆脱应届生那个标签。

最后说一句鸭鸭自己的看法。

学校禁 AI 不一定错,公司逼 AI 也不一定对。真正错的,是没人帮应届生处理这中间的认知断层。

你能做的,就是自己提前两年开始切换。等别人还在为学术不端吓得不敢碰 AI 的时候,你已经在用 AI 帮自己刷题、模拟面试、写简历、改面经了。

毕业那天的差距,就是这两年攒出来的。

你们怎么看这事?大学和公司的 AI 规则,你觉得到底谁更对?评论区聊聊。

今天鸭鸭和大家分享一道 后端场景题面试题。

【编写一段代码,使得这段代码必定会产生死锁,不能使用Thread.sleep() 】

回答重点

关键是要确保两个线程同时持有各自的锁,然后再去争抢对方的锁,这样就能稳定复现死锁

CountDownLatch就能做到。

核心思路是:

  1. 创建一个计数为 2 的 CountDownLatch

  2. 线程 1 先拿到 lock1,执行 countDown 减一,然后 await 等待

  3. 线程 2 先拿到 lock2,执行 countDown 减一,然后 await 等待

  4. 两个线程都到达 await 后,同时被唤醒

  5. 线程 1 想要 lock2,但 lock2 被线程 2 持有

  6. 线程 2 想要 lock1,但 lock1 被线程 1 持有

  7. 互相等待对方释放锁,死锁必然发生

打开网易新闻 查看精彩图片

代码实现:

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 和 CyclicBarrier 的区别

CountDownLatch 是倒计时门闩,构造时传入一个计数值,每次调用countDown()减一,当计数归零时所有调用await()的线程同时被唤醒。特点是一次性使用,用完就废了。

CyclicBarrier 是循环栅栏,构造时传入一个参与线程数,每个线程调用await()后会阻塞,直到所有线程都到达栅栏位置,然后一起被释放。特点是可以复用,释放后会重置计数,可以继续使用。

对于制造死锁来说,两者都能用,但 CountDownLatch 更直观,因为它天然就是"等所有人准备好再一起开始"的语义。CyclicBarrier 也可以,但它的重置特性在这里用不上。

如何检测和避免死锁

线上环境如果怀疑发生了死锁,可以用jstackjconsole查看线程堆栈,JVM 会自动检测死锁并在堆栈信息里标注出来。

避免死锁的常见手段:

1)加锁顺序一致:所有线程按照相同的顺序获取多个锁,比如都先拿 lock1 再拿 lock2,这样就不会出现循环等待。

2)尝试加锁超时:用ReentrantLock.tryLock(timeout)替代 synchronized,拿不到锁就放弃已持有的锁,过一会重试。

3)减少锁粒度:不要一把大锁锁住整个方法,尽量缩小锁的范围,减少持有锁的时间。

4)避免嵌套加锁:如果业务允许,尽量不要在持有一把锁的情况下再去获取另一把锁。

篇幅有限,完整答案可以点击下方小程序进行查阅

我们精选了近两年的高频面试真题,已经有 10000 多道面试题目啦,由大厂资深面试官手写答案,押题命中率超高!

不仅有传统八股文,场景题、项目题、系统设计题等等应有尽有,还在不断更新中!

目前优惠最低特价 129 元即永久(限时上架)畅看所有面试题和答案,正式运营价格为 399+,不要错过这次优惠哈!

且,现在邀请好友注册并成为会员,还可获得 10% 的分佣!详情见面试鸭拉新邀请有赏规则(网页版面试鸭点击头像查看)

打开网易新闻 查看精彩图片

网页端网址:www.mianshiya.com

打开网易新闻 查看精彩图片