UNIX环境高级编程-读书进度记录

第1章 UNIX基础知识 结构:内核 -> 系统调用 -> shell + 公用函数库 -> 应用程序 登录,/etc/passwd文件中的内容, shell的多个实现版本,bash, dash, zsh… 绝对路径、相对路径 输入和输出 (第三章详细) 每运行一个新程序时,所有shell为其打开3个文件描述符:标准输入、标准输出、标准错误。若不特殊处理,都链接向终端。 不带缓冲的IO:open, read, wirte, lseek, close,read时自己指定BUFFERSIZE,不同BUFFERSIZE导致不同程序效率 (第三章详细说明) 标准IO:为不带缓冲的IO函数提供了一个带缓冲的接口。使用标准IO函数,无需担心如何选取最佳缓冲区(上述BUFFERSIZE)的大小。 程序和进程 (第十二章详细) 程序存储在磁盘上,内核使用exec函数(7个exec函数之一)将程序读入内存并执行 getpid获取进程id...

后端工程师的自我修养

半年前,我致力于成为Web后端工程师,当初在知乎上看到一句话:人生苦短,这么多有意思的方向,何必要做web(大意如此,web后端地位之低可见一斑)。经历到现在,才略懂。web后端的确是没挑战的一份岗位,常见的就是面向业务逻辑CRUD编程,不要说高并发、C10K问题,绝大部分人写的还是面向内部人员的web吧,别说1w并发,日访问量能有1w都罕见(当然我很期待以后有这样的实际需求抛给我,以打破我对web的错误认知)。那么后端工程师,尤其是Java工程师,资深和新手程序员其差别在何处? 仅以我这一个月的零碎见闻来看,有: 主流框架、中间件相当熟练的应用,包括但不限于netty, redis, Spring, ORM(MyBatis等)quartz, 线程池ExecutorService。 设计模式、抽象类、接口的广泛应用,以我目前水平看来甚至有过度设计的嫌疑。整体设计上很规范。 Java语言本身的特性很熟,譬如反射,各种锁用的也很熟练。 对于相对常见的需求,有相对成熟的解决方案。譬如监控、譬如任务调度。 因工作经验丰富,对常见的单点登录(SSO)等各大公司通用的解决方案熟悉。 思路清晰,看穿你的技术细节。 总结一下,其实就是:时间带来的熟练度的提升,经验带来的给出方案的能力,下功夫和思考以挖掘深度。这是技术上的修养,而在非技术方面,学会更有效地沟通和表达则是工程师不可或缺的能力。 站在新人的角度,尽量多学习、多思考,以及质疑,因为他们做的或说的也有很多错的。 硬实力 效率&生产力 Linux与shell脚本,awk sed文本处理命令,vim命令,常用部署命令(tomcat)、常用软件。 git,我用了几年git也基本没超出 add, commit, push, pull, reset这几个范畴。 Python的熟练使用与进阶。 前端 前后端分离方案。 cookie与session(有些前端对此都不够了解)。 静态代理与转发。 数据库...

jdk-LinkedHashMap浅显探索

LinkedHashMap是继承自HashMap的子类,功能上是迭代遍历时按插入顺序(默认)或LRU顺序,这个由accessOrder这个参数初始化LinkedHashMap时指定。 哈希表的实现复用了HashMap,迭代遍历顺序是通过给每个Node增加一个前后指针(before, after)并控制连接关系,在整个连接中保留首尾指针(head, tail)来实现顺序遍历,这个顺序在初始化时通过指定accessOrder就决定了是插入顺序还是LRU顺序。 初始化 跟HashMap类似,可以不指定、指定初始容量、指定初始容量+负载因子,除此之外,LinkedHashMap可指定accessOrder(布尔值),即访问顺序,默认false为插入顺序,true为LRU顺序。 get方法 重写了HashMap的get方法,getNode之后,如果accessOrder为true,调用afterNodeAccess方法。 put方法 没重写,复用HashMap的put方法。 remove方法 没重写,复用。 clear方法 重写,除调用HashMap.clear外,还把首尾指针都置空。 下面是HashMap源码中的三个空函数,注释中写明了是为了给LinkedHashMap回调用。 // Callbacks to allow LinkedHashMap post-actions void afterNodeAccess(Node<K,V> p) { } void afterNodeInsertion(boolean evict) {...

jdk-HashMap浅显探索

本文探索最常用到的方法的基本实现,其中关于红黑树的各种操作,这里不涉及。 先说下,HashMap底层是数组+链表/红黑树,即Node[]数组,Node是一个key-value的封装类,还有指向下一Node的指针。当HashMap中的元素数量超过capacity * loadFactor之后,进行resize,resize对于空数组,进行初始化分配,否则就进行2倍扩容和rehash。get时,直接拿table[key.hash & (n-1)]的值,若不是单Node,则通过链表/红黑树去找。put时,也是先看table[key.hash & (n-1)]是否为空,若不为空,则通过链表或红黑树插入,链表插入还需要考虑到>8转红黑树,以及插入完看是否需要resize。remove和put类似,只不过是个相反过程。 几个关键数值: 初始容量(2^4) 最大容量(2^30) 默认的负载因子(0.75) Treeify阈值,超过8后链表转红黑树 UnTreeify阈值,小于6树转链表 Node类,实现了Map.Entry接口,把 <K, V>封装起来,构成Node链表。 暴露出去的公共方法: 初始化,多个重载版本,可不指定/指定初始容量/指定初始容量+负载因子,或以另一个map去初始化此map get(key),调用final getNode containsKey,判断上面getNode返回值是否为空 put,调用final putVal方法 remove,调用final removeNode方法 clear,遍历table,table[i] = null containsValue,遍历table,再遍历每个桶...

jdk-AQS探索

AQS是一个基于FIFO的框架,我是在ReentrantLock的实现中去理解AQS的acquire过程。这里先看下AQS的acquire,再对ReentrantLock的公平锁/非公平锁尝试分析。 public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } tryAcquire是AQS的一个接口,在ReentrantLock中有 FairTryAcquire和NonFairTryAcquire两种,略有差别,但都是一次尝试返回true/false,这里如果一次获取不到,则执行后面。 addWaiter(node),把当前node设成独占模式,插入到双向链表的尾部,CAS自旋来保证线程安全。 acquireQueued,CAS自旋等待,直到当前node为head的后继结点并tryAcquire成功。 非公平锁的tryAcquire protected final boolean tryAcquire(int acquires) { // 调用非公平锁的tryAcquire return nonfairTryAcquire(acquires); }...