Low Gravity

在这里,我们用算法解决问题,用代码表达思想

C10K 讨论的是一台服务器如何同时维持一万条网络连接。这里的“一万”指并发连接数,不等同于每秒完成一万次请求。放到今天,这个数量并不夸张;但在“一条连接对应一个进程”的服务模型里,连接数增加意味着进程数也随之增加,服务器很快就会把大量资源花在连接之间的调度上。

I/O 多路复用改变的正是这一点:程序不再为每条连接准备一个独立进程,而是让少量线程同时观察大量文件描述符,只处理已经具备读写条件的连接。selectpollepoll 都在解决这个问题,只是随着连接数上升,它们查找就绪事件的成本差别很大。

阅读全文 »

多个线程共同操作一份状态时,仅仅保证“一次只有一个线程修改”还不够。消费者拿到锁以后可能发现队列为空,生产者拿到锁以后也可能发现队列已满;它们需要在条件不成立时暂停,并在状态变化后继续竞争执行机会。

管程把共享状态、访问状态的操作、互斥规则和条件等待放进同一个抽象里。Java 的 synchronizedwait/notify 组合正体现了这套思路:前者保护状态,后者协调线程什么时候可以继续。

阅读全文 »

一个静态文件已经保存在磁盘上,服务端只是把它原样发送给客户端。业务代码没有解析或修改文件内容,但如果使用普通的 read()write(),数据仍会经过用户缓冲区,在内核与用户空间之间来回搬运。

DMA 让存储设备和网卡可以直接与内存交换数据,零拷贝则进一步减少 CPU 参与的内存复制。理解这两项技术,关键不是记住“几次拷贝、几次切换”的结论,而是沿着数据路径分清:哪一步由设备完成,哪一步由 CPU 完成,用户程序是否真的需要拿到文件内容。

阅读全文 »

Java 的自动内存管理主要是针对对象内存的回收和对象内存的分配。同时,Java 自动内存管理最核心的功能是 内存中对象的分配与回收。

Java 堆是垃圾收集器管理的主要区域,因此也被称作GC 堆(Garbage Collected Heap).从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以 Java 堆还可以细分为:新生代和老年代:再细致一点有:Eden 空间、From Survivor、To Survivor 空间等。进一步划分的目的是更好地回收内存,或者更快地分配内存。

阅读全文 »

Redis 分布式锁最危险的时刻,往往不是加锁失败,而是锁已经过期,旧的持有者却还在继续执行。

客户端 A 拿到一把有效期 10 秒的锁,业务执行中因为 GC、网络阻塞或外部接口超时停顿了 15 秒。租约到期后,客户端 B 成功加锁并开始工作。此时 A 恢复运行,两个客户端都会操作同一份资源。即使加锁和解锁命令写得完全正确,这个时间窗口仍然存在。

理解这一点以后,Redis 锁的安全边界才比较清楚:它可以协调并发,却不能独自保证共享资源永远只接受当前持有者的操作。

阅读全文 »
0%