程序员老鬼

大厂员工:公司花月薪6万,挖来一位技术大牛,结果入职还没满两个月就和部门负责人正面杠上了。导火索只有一个,他不怎么加班!

刚看到个贴子,说大厂花月薪6万挖来个技术大牛,结果不到两个月就跟部门负责人正面刚,原因就一个:人家不爱加班。

Image

网友们的回复我看了看,有的说“拿人钱就得多干活,多加班天经地义”;也有人觉得“技术牛就该按劳动法来,公司别拿情怀压人”。

我觉得这事吧,关键在“预期没对齐”。公司开6万,心里想的是“买断你的时间和态度”;大牛以为的是“你花的是我专业能力的钱,不是命”。放一起,不吵才怪。真正健康的职场,是入职前把加班、KPI、边界都说清楚:愿意加班的就拿高强度高回报路线,不愿意加班的就用结果说话,按产出评估。

面试题:交替打印字符串

昨天晚上十一点多,我在公司楼下啃着个凉了的煎饼,我们组那个小李一脸生无可恋跟我说:东哥,今天面试又被干倒了,就那道“两个线程交替打印字符串”的题,我又写崩了。 我一听这玩意,太常见了嘛,聊着聊着就顺手在他电脑上敲了一版 Java,顺便跟他说了下这题到底在考啥。

他那个场景是这样的: 有两个字符串,比如 "ABC" 和 "123",希望用两个线程交替打印出:A1B2C3,就像俩人你一句我一句说话一样,中间不能抢话,也不能冷场。

这事儿用 Java 写,看着好像就是 System.out.print,其实背后考的都是多线程那点破事:共享变量、锁、wait/notify,那些你们平时一眼带过的地方。

我当时就跟小李说:你先别着急想算发,先想生活里的场景。 就像过红绿灯,一个人是“红灯管家”,一个人是“绿灯管家”,轮流放行。只有轮到你这边是绿灯,你才能走;走完了记得把灯切回去,喊一嗓子“换你了”。

在代码里,其实就三件事:

1)要有一个“共同的红绿灯”对象,大家都在它身上 wait / notify2)要有一个状态位,记录到底轮到谁打印 3)每次打印前先看状态,不是你就老老实实等,是你就打印完改状态再叫醒别人

给你看下我当时随手写的那版,别在意变量名,有点随意:

publicclassAlternatePrinter{

privatefinal Object lock = new Object();  // 公共红绿灯
privateboolean turnLetter = true;         // true 打字母,false 打数字

privatefinalchar[] letters;
privatefinalchar[] numbers;

publicAlternatePrinter(String letters, String numbers){
// 简单粗暴一点,不考虑长度不等的情况了
this.letters = letters.toCharArray();
this.numbers = numbers.toCharArray();
    }

publicvoidprintLetters(){
for (int i = 0; i < letters.length; i++) {
synchronized (lock) {
while (!turnLetter) {  // 不是我就等
try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
                System.out.print(letters[i]); // 打印当前字母
                turnLetter = false;           // 轮到数字
                lock.notifyAll();             // 叫醒对面那个线程
            }
        }
    }

publicvoidprintNumbers(){
for (int i = 0; i < numbers.length; i++) {
synchronized (lock) {
while (turnLetter) {  // 轮到字母的时候我就先别吭声
try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
                System.out.print(numbers[i]); // 打印当前数字
                turnLetter = true;            // 再切回字母
                lock.notifyAll();             // 继续叫醒
            }
        }
    }

publicstaticvoidmain(String[] args){
        AlternatePrinter printer = new AlternatePrinter("ABC", "123");

        Thread t1 = new Thread(printer::printLetters, "letter-thread");
        Thread t2 = new Thread(printer::printNumbers, "number-thread");

        t1.start();
        t2.start();
    }
}

小李看到这里说:东哥,这不就 synchronized + wait/notify 那套么,有啥讲究啊? 我说讲究可多了,你面试的时候老是翻车,大概率就翻在这些细节上了。

比如那个 while,很多人图省事写成 if,在面试题里有时候也跑得起来,但真实环境下一来个“虚假唤醒”,线程被唤醒了发现条件其实还不满足,如果你是 if 判断,它就直接往下走了,顺序立马乱套。 所以模板就是:while (条件不满足) wait(),这个真是看到脑袋疼那个模板,但是没办法,写多线程就得啃下来。

还有一个点,小李一开始写的是两个 synchronized(this),然后里面又 new 了锁对象,整得可乱了。 我跟他说:你就当有个“会议室”就行,两个线程必须进同一个会议室里商量,要不根本谈不上“交替”。所以那个 lock 一定要是同一个对象。

再往深一点说,其实可以换成 ReentrantLock + Condition,语义更清楚,一个 Condition 管字母,一个 Condition 管数字,各等各的信号,不过面试官一般看到你把 wait/notify 写清楚了,也就不会继续折腾你了。真要写的话,大概长这样,给你看个简化版的:

classAlternatePrinter2{
privatefinal java.util.concurrent.locks.Lock lock =
new java.util.concurrent.locks.ReentrantLock();
privatefinal java.util.concurrent.locks.Condition letterCond =
            lock.newCondition();
privatefinal java.util.concurrent.locks.Condition numberCond =
            lock.newCondition();

privateboolean turnLetter = true;
privatefinalchar[] letters = "ABC".toCharArray();
privatefinalchar[] numbers = "123".toCharArray();

publicvoidprintLetters(){
for (char c : letters) {
            lock.lock();
try {
while (!turnLetter) {
                    letterCond.await();
                }
                System.out.print(c);
                turnLetter = false;
                numberCond.signal();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                lock.unlock();
            }
        }
    }

publicvoidprintNumbers(){
for (char c : numbers) {
            lock.lock();
try {
while (turnLetter) {
                    numberCond.await();
                }
                System.out.print(c);
                turnLetter = true;
                letterCond.signal();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                lock.unlock();
            }
        }
    }
}

这个就更像是两个微信群,各有各的“@”通知,但底层还是那套思想:一个状态位 + 一个锁 + 两个条件队列,谁不该说话就老实排队。

我跟小李说,你以后再遇到这种“交替打印”的题,脑子里就想三句话: 有个共同的锁,有个轮流的标记,打印完记得叫醒别人。 不管是 A/B 交替,还是 ABC 三个线程循环,都是在这上面加花活而已。

说着说着时间就快十二点了,保安又来催人走,我电脑一合,才想起来我那杯奶茶都结冰渣了…算了不想说了,我得上去把明天早会那个 demo 补完,不然又要被产品叨叨。

-END-

我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html

最后给大家分享一份不错的副业资料,点击下方公众号,回复关键字: 副业 领,也可以链接我微信:hls404