程序员老鬼

公司趁我下班后偷看我电脑,看到我电脑里有信息,git拉的代码说我干私活要把我开了,最后一万二的工资最后扣了只剩四千。。

刚刷到个贴子,说有人下班后公司偷偷翻他电脑,看到git代码就扣帽子说干私活,要开人,还把一万二的工资扣到只剩四千😓。

Image

作为程序员我觉得这事太典型了,很多老板完全不懂技术,就凭几个文件就下结论,真的是外行指导内行。更离谱的是,擅自查看员工电脑,本身就是严重侵犯隐私的行为,换到大厂,分分钟吃官司。网友评论有的劝他赶紧维权,有的说认栽算了,我更认同前者——不合理的事一次妥协,以后就会变成常态。

从技术人的角度看,写代码只是工作的一部分,但更重要的是专业信任。公司如果一边逼你加班,一边又用莫须有的理由克扣工资,这不叫管理,这是赤裸裸的PUA。再说了,工资不是施舍,是劳动换来的,少一分都算欠账。

总的来说,遇到这种黑心公司,别犹豫,留证据走法律途径,代码可以重写,身体和尊严不能被透支。【备注:文末可领最新资料】

算法题:函数防抖

昨晚十一点多,我在公司楼下吹风,手机那搜索框老是卡卡的,你们懂吧,手一抖就连敲好几下,结果接口被打爆。那个...怎么说呢,这种时候就该上“函数防抖”。别怕名词,防抖就是——你别急,我等你手停下来再干活,省得白忙活。

想象一下电梯关门按钮,有人狂按,电梯不会每按一下就关一次门,对吧?它会等最后一次,确认没人再伸手,咔叽关上。输入框联想、按钮防重复提交、窗口resize重排,这些都适合。和“节流”不一样啊,节流是隔段时间做一次,防抖是等安静下来再做一次。有人问区别?简单说:防抖追求“最后一次最准确”,节流追求“按频率稳定输出”。嗯差不多这个味。

我用的是 ScheduledExecutorService,因为它能很方便地设个“定时炸弹”,每次有人来触发,就先把上一个炸弹拆掉,重新计时。时间一到没再被打断,就执行。代码我贴一下,别嫌长,能用就行:

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;

publicclassDebouncer{
privatefinal ScheduledExecutorService pool;
privatefinallong delayMillis;
privatefinal AtomicReference<ScheduledFuture<?>> futureRef = new AtomicReference<>();
privatefinalboolean leading;   // 立即触发一次?
privatevolatilelong lastInvoke = 0L;
privatefinal Object lock = new Object();

publicDebouncer(long delayMillis){
this(delayMillis, false);
    }

publicDebouncer(long delayMillis, boolean leading){
this.delayMillis = delayMillis;
this.leading = leading;
this.pool = Executors.newSingleThreadScheduledExecutor(r -> {
            Thread t = new Thread(r, "debouncer");
            t.setDaemon(true);
return t;
        });
    }

publicvoidsubmit(Runnable task){
long now = System.currentTimeMillis();
synchronized (lock) {
// leading 模式:第一次就干,后面在窗口内不再触发
if (leading && (now - lastInvoke) >= delayMillis) {
                lastInvoke = now;
                task.run();
                cancelScheduled();
return;
            }
            cancelScheduled();
            ScheduledFuture<?> f = pool.schedule(() -> {
synchronized (lock) {
                    lastInvoke = System.currentTimeMillis();
                }
                task.run();
            }, delayMillis, TimeUnit.MILLISECONDS);
            futureRef.set(f);
        }
    }

publicvoidcancel(){
synchronized (lock) {
            cancelScheduled();
        }
    }

publicvoidflush(Runnable task){ // 立刻执行最后一次
synchronized (lock) {
            cancelScheduled();
            lastInvoke = System.currentTimeMillis();
            task.run();
        }
    }

privatevoidcancelScheduled(){
        ScheduledFuture<?> f = futureRef.getAndSet(null);
if (f != null) f.cancel(false);
    }

publicvoidshutdown(){
        pool.shutdownNow();
    }
}

对了刚才说到哪…哦,使用方式特别直白。比如输入框在 onChange 里:

Debouncer db = new Debouncer(300); // 300ms
// 每次文本变更
db.submit(() -> searchApi.call(textField.getText()));

这样用户噼里啪啦敲键盘,只有停下来的那一刻才去请求。要是担心第一次也得等?把构造函数的 leading 设成 true,首次就先给一次,后面300ms内不再触发,新一轮再说。有人问按钮连点的问题,我一般也是包一层:

Debouncer clickDb = new Debouncer(800, true);
button.addActionListener(e -> clickDb.submit(() -> payOrder()));

我现在有点困…但这个得强调:线程问题。上面用的是单线程调度器,任务里如果要更新Swing/JavaFX界面,记得丢回UI线程,比如 SwingUtilities.invokeLater(...) 或 Platform.runLater(...),不然就那种闪一下崩一下的诡异bug。还有超时太短没意义,打个喷嚏就触发;太长又像卡死。经验值:输入框 200400ms,窗口resize 100200ms,提交按钮 500~1000ms 看业务。

另外两个小函数很救命:cancel() 用在页面销毁或者切路由,别让旧任务尾随;flush() 在用户点“立即搜索”的时候,直接把最后一次执行掉,别再等。

-END-

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

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