毕业那天,你学的东西就被推翻了
最近鸭鸭在脉脉看到一条提问,看完后愣了一下:
“为什么大学禁止用 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就能做到。
核心思路是:
创建一个计数为 2 的 CountDownLatch
线程 1 先拿到 lock1,执行 countDown 减一,然后 await 等待
线程 2 先拿到 lock2,执行 countDown 减一,然后 await 等待
两个线程都到达 await 后,同时被唤醒
线程 1 想要 lock2,但 lock2 被线程 2 持有
线程 2 想要 lock1,但 lock1 被线程 1 持有
互相等待对方释放锁,死锁必然发生
![]()
代码实现:
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 也可以,但它的重置特性在这里用不上。
如何检测和避免死锁
线上环境如果怀疑发生了死锁,可以用jstack或jconsole查看线程堆栈,JVM 会自动检测死锁并在堆栈信息里标注出来。
避免死锁的常见手段:
1)加锁顺序一致:所有线程按照相同的顺序获取多个锁,比如都先拿 lock1 再拿 lock2,这样就不会出现循环等待。
2)尝试加锁超时:用ReentrantLock.tryLock(timeout)替代 synchronized,拿不到锁就放弃已持有的锁,过一会重试。
3)减少锁粒度:不要一把大锁锁住整个方法,尽量缩小锁的范围,减少持有锁的时间。
4)避免嵌套加锁:如果业务允许,尽量不要在持有一把锁的情况下再去获取另一把锁。
篇幅有限,完整答案可以点击下方小程序进行查阅:
我们精选了近两年的高频面试真题,已经有 10000 多道面试题目啦,由大厂资深面试官手写答案,押题命中率超高!
不仅有传统八股文,场景题、项目题、系统设计题等等应有尽有,还在不断更新中!
目前优惠最低特价 129 元即永久(限时上架)畅看所有面试题和答案,正式运营价格为 399+,不要错过这次优惠哈!
且,现在邀请好友注册并成为会员,还可获得 10% 的分佣!详情见面试鸭拉新邀请有赏规则(网页版面试鸭点击头像查看)
![]()
网页端网址:www.mianshiya.com
![]()
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.