2018第24周总结
本周写了AQS原理及相关应用的学习摘录。它是高级Java开发应该掌握的基础,虽然在当前浮躁的社会背景下,知道这些原理对实际的工作、对个人工作中创造的价值可能没大的影响。学习并在工作中模仿应用这里面设计、思想,可以让你在未来的技术路上走的更远。
AQS目的是为构建同步锁、同步组件提供基础框架,原理是通过volatile int型变量抽象同步状态,变量值为0表示锁空闲,正数值表示锁占用,然后通过内部类Node构建FIFO的同步队列实现等待获取同步状态的线程排队工作,通过内部类ConditionObject构建条件等待队列,完成等待条件线程的排队工作。
AQS设计上、采用模板方法模式构建,其内部提供了并发操作的核心方法、而将一些不同模式下实现可能有差异的操作定义为模板方法,让其子类实现。如ReentrantLock通过内部类Sync及其子类继承AQS实现tryAcuire()和tryRelease()方法来实现独占锁,而SemaPhore则通过内部类继承AQS实现tryAcquireShared()方法和tryReleaseShared()方法实现共享模式锁。
读写锁里面同时包含了共享锁【读锁】和排他锁【写锁】,为此它在基于AQS实现时,扩展了同步状态变量的意义,用这个状态高位部分表示读锁,低位表示写锁。由于读锁是共享锁会有多个,要把线程重入读锁的次数保存在ThreadLocal变量里,而写锁低16位可以表示写锁的重入次数。
读写锁多用在读多写少的场景,而这也是大部分现实生活中的应用场景,就像我们每个人日常都是读多写少一样。同样在容器实现为避免使用锁,进一步提高效率,可以使用CopyOnWrite策略,JDK本身有提供它的实现,默认容器只能读,但有写操作时复制一份而不是加锁控制,它在多读写少的场景下能提高程序运行效率,但它不能做到实时一致性,并且会占用更多的内存。
java并发之阻塞队列LinkedBlockingQueue与ArrayBlockingQueue
讲了这两种阻塞的队列的实现原理及差异。由于底层数据结构前者用链表后者用数组,所以前者可以用两个锁和两个条件【对put和take分别加锁,提高了吞吐率,但put速度大于take速度时可能会造成内存溢出要多注意】,后者使用了一个锁和两个条件【对put、take操作使用排它锁控制,同时有两个条件notEmpty和notFull来实现put、take操作的阻塞和唤醒控制】。