java并发学习 读书笔记一

Java并发编程实践
电子工业出版社

读书笔记:
1、volatile变量理解
   写入volatile变量时就像退出同步块,读取volatile变量时就像进入同步块。volatile变量使用也存在好多限制,它通常被当作完成、中断、状态的标记使用。volatile变量只保证可见性,但不保证原子性。所以自增操作就不能依赖volatile,除非只有一个线程对它进行写操作。
   不能用于构建原子化的复合操作,这意味着当一个变量依赖其他变量时,或者当变量的新值依赖于旧值时,是不能用volatile变量的。

2、可重进入的理解(Reentrancy)
   当一个线程请求其他线程已经占有的锁时,请求线程会被阻塞。然而内部锁是可重进入的,因此线程在试图获得自己占有的锁时,请求会成功。重进入意味着锁的请求是基于“每线程的”,而不是“每调用的”。重进入的实现是通过为每个锁关联一个请求计数和一个占有它的线程。当计数为0时,认为锁是未被占有的。线程请求一个未被占有的锁时,JVM将记录锁的占有者,并且将请求计数置为1.如果同一线程再次请求这个锁,计数将递增;每次占用线程退出同步快,计数递减,到0锁就释放。

3、Collections.synchronizedMap() 和 ConcurrentHashMap的区别
  它们一样是一个哈希表,但采用了不同的锁策略:前者使用公共锁同步每一个方法,并严格的限制只有一个线程能访问容器。后者采用一个更加细化的锁机制,名叫分离锁。
  前者同步提供了一个特性就是为独占的访问加锁,这在后者中并没有实现。这对于一些罕见的情况是必要的,例如原子的加入一些mapping,或者对元素进行若干次迭代,在这期间需要看到元素以相同的顺序出现。

4、CopyOnWriteList/CopyOnWriteSet 写入时复制(copy on write)原理解析
  "写入时复制"容器的线程安全性来源于这样一个事实,只要有效的不可变对象被正确的发布,那么访问它将不再需要更多的同步。在每次需要修改时,它们会创建并重新发布一个新的容器拷贝,以此来实现可变性。"写入时复制"容器保留一个底层基础数据的引用。这个数组作为迭代器的起点,永远不会被修改,以尼茨对它的同步只不过为了确保数组内容的可见性。它返回的迭代器与创建时保持一致,不会考虑后续的修改。
  适合对容器迭代的频率远远高于对容器修改的频率。

5、阻塞队列 与 生产-消费者模式
阻塞队列接口 BlockQueue 支持两个附加操作的Queue,这两个操作是:获取元素时等待队列变为非空,以及存储元素时等待空间变得可用。

BlockingQueue 方法以四种形式出现,对于不能立即满足但可能在将来某一时刻可以满足的操作,这四种形式的处理方式不同:第一种是抛出一个异常,第二种是返回一个特殊值(null 或 false,具体取决于操作),第三种是在操作可以成功前,无限期地阻塞当前线程,第四种是在放弃前只在给定的最大时间限制内阻塞。下表中总结了这些方法:

     抛出异常 特殊值 阻塞 超时
插入 add(e) offer(e) put(e) offer(e, time, unit)
移除 remove() poll() take() poll(time, unit)
检查 element() peek() 不可用 不可用


它的实现:LinkedBlockingQueue和ArrayBlockingQueue是FIFO队列
        PriorityBlockingQueue是按照优先级顺序排序的队列
        SynchronousQueue它根本不是一个真正的队列,因为它不会队列元素维护任何存储空间,不过它维护一个线程清单。适合消费者充足的情况,它们总能为下一个任务做好准备。

6、双端队列和窃取工作
  Java 6同样新增了两个容器类型,Deque(发音为deck)和BlockingDeque,它们分别扩展了Queue和BlockingQueue。Deque是一个双端队列,允许高效地在头和尾分别进行插入和移除。实现它们的是ArrayDeque和LinkedBlockingDeque。
  双端队列使它们自身与一种叫做窃取工作的模式相关联。在窃取工作的设计中,每一个消费者都有一个自己的双端队列。如果一个消费者完成了自己双端队列中的全部工作,它可以偷取其他消费者的双端队列中的末尾任务,这样可以保持每个线程都保持忙碌的状态。

7、延迟周期执行工具:Timer和新的ScheduledThreadPoolExecutor的比较
  在JDK5或者更高的版本中,不建议使用Timer。
原因分析:
  Timer对调度的支持是基于绝对时间的,而不是相对时间的,由此任务对系统时钟的改变是敏感的;而ScheduledThreadPoolExecutor只支持相对时间。
  Timer只创建唯一的线程来执行所有的Timer任务。
  Timer还有一个问题在于,如果TimerTask抛出未检查的异常,Timer将产生无法预料的行为。Timer线程并不捕获异常,所以TimerTask抛出的未检查异常会终止Timer的执行,而且Timer也不会再重新恢复线程的执行,它错误的认为整个Timer都被取消了。

你可能感兴趣的:(编程,出版)