diff --git "a/second/week_04/74/\347\254\254\345\233\233\345\221\250\344\275\234\344\270\232.txt" "b/second/week_04/74/\347\254\254\345\233\233\345\221\250\344\275\234\344\270\232.txt" new file mode 100644 index 0000000000000000000000000000000000000000..85ee2e1d7e072dfb119290ee0fcc7e765549490b --- /dev/null +++ "b/second/week_04/74/\347\254\254\345\233\233\345\221\250\344\275\234\344\270\232.txt" @@ -0,0 +1,40 @@ +并发集合 + +CopyOnWriteArrayList: 实例进行修改操作(add/remove等)会新建一个数据并修改,修改完毕之后,再将原来的引用指向新的数组。 + 修改过程没有修改原来的数组。也就没有了ConcurrentModificationException错误 + 增删方法同步防止copy多个副本,读取不加锁 + + 缺点:增删过程中内存占用double 只保证数据最终一致性,不保证实时一致性。 + +ConcurrentHashMap: + 结构:哈希数组 + 链表/红黑树,key和value均不能为null + 操作方式与HashMap基本一致,concurrentHashMap是线程安全的,通过unsale实现无锁编程 + + 这些原子操作保证了ConcurrentHashMap的线程安全。 + + // 获取tab数组的第i个node + static final Node tabAt(Node[] tab, int i) { + return (Node)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE); + } + // 利用CAS算法设置i位置上的node节点。在CAS中,会比较内存中的值与你指定的这个值是否相等,如果相等才接受 + static final boolean casTabAt(Node[] tab, int i, + Node c, Node v) { + return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v); + } + // 利用volatile方法设置第i个节点的值,这个操作一定是成功的。 + static final void setTabAt(Node[] tab, int i, Node v) { + U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v); + } + + +ArrayBlockingQueue:基于数组(循环数组)的阻塞队列,常用操作 add, offer, put, remove, poll, take, peek + 有界数组,创建时必须指定列队大小 + + 通过源码分析,我们可以发现下面的规律: + + 阻塞调用方式 put(e)或 offer(e, timeout, unit) + 阻塞调用时,唤醒条件为超时或者队列非满(因此,要求在出队时,要发起一个唤醒操作) + 进队成功之后,执行notEmpty.signal()唤起被阻塞的出队线程 + 出队的源码类似。ArrayBlockingQueue 队列我们可以在创建线程池时进行使用。 + + new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue(2));