最近一直把这本书在看着,看了有一阵子了大概已经过了两遍了,不知道是不是因为自己基础不够扎实总觉得看的好多东西还是理解不了,所以继续写博客进行总结,继续对本书进行死磕。
第五章主要讲述了基础构建模块即就是讲解了一些关于java同步工具类以及安全线程类的相关基础知识。
第一部分同步容器类
1.引出同步容器类的问题
一个线程请求某个容器类中的对象,另一个线程中将此对象删除,这时候请求线程将抛出异常,因为请求的索引值已经不再有效。第二个问题关于迭代器与ConcurrentModificationException容器在使用迭代器迭代访问时由于之前的问题,存在并发的修改容器内容,此时就会抛出该异常。要避免此类问题,必须在迭代过程中持有容器的锁。但是由于持有容器操作的效率太低,所以另一种替代的办法是使用克隆容器,即在容器的副本上进行操作这种方法也存在性能上的开销,具体的好坏要取决于使用者的自身需求。当然在有些情况下,使用者调用了带有迭代性质的方法,此时也会抛出上述异常,这就要求使用者在编写并发代码时要注意多多思考。
2讲解了并发容器的相关知识
①concurrentHashMap:此并发容器与HashMap类似是一个基于散列的Map,它使用分段锁机制实现更大程度的共享,使得有更多的线程可以并发的访问map,他的迭代器具有弱一致性因此不会抛出currentModificationException,虽然具有很多优点但是他还是存在有一些不足的地方,例如想要查看他的容量,此时concurrent返回的会是一个估计值,如果需要使用加锁独占式的访问map的情况下,使用次容器将不再合适,对于ConcurrentHashMap底层有兴趣的同学可以看这篇文章,讲解的非常仔细http://blog.csdn.net/u010723709/article/details/48007881
②CopyOnWriteArrayList:此容器用于替代同步的list,并且在迭代期间不需要对容器进行加锁或者复制。同样的类似的set代替同步的set。这些容器采用写入时复制的机制,即只要正确而的发布了一个事实不可变对象(线程安全的对象)那么在访问该对象时候就不需要再进行同步操作。而在每次修改的时候都会创建一个副本,然后对副本进行修改操作。此类容器的迭代器保存了一个指向底层数组的引用,这个数组位于迭代器的初始位置而且不会被修改,因此只要确保了数组中内容的可见性即可,这样就保证了可以使用多线程访问该类容器并且各线程之间不会存在干扰问题。但是这类容器的缺点也是很明显的,因为每次的修改都要创建一个副本,开销也是很大的,所以要这类容器适合迭代操作很多的情况下使用,但是如果你不想的话,那我也没办法哈哈哈哈。
3.多线程中的生产者消费者模式:
生产者消费者模式是多线程中一种很常见的模式,这类模式在本书中也有很详细的讲解。生产者消费者模式是利用阻塞队列作为容器。生产者使用put或者offer方法将内容放入到阻塞队列当中,而消费者使用take或者poll方法对放入队列中的对象进行使用,如果生产方法放入对象过快导致使用者没有办法进行及时的处理,此时生产方法会被阻塞,知道消费方法吧内如处理完为止,反之亦然,其中put与take方法会抛出异常,而另两个则不会。在阻塞队列中有先进先出形式的LinkedBlockingqueue以及ArrayBlockingQueue也有类似于PrioiotyBlockingqueue的优先级阻塞队列,也有直接将任务从生产者直接交到消费者而不放入队列的SynchronousQueue,如果希望消费者线程在处理完自己的任务后可以处理别的消费者任务那么deque是一个不错的选择。
4.关于阻塞方法和中断方法
正如之前上说道的put与take方法会抛出异常,这类方法通常会抛出InterruptedException,一般情况下抛出此类异常的方法都是阻塞方法,这类方法如果被中断那么通常会努力提前结束阻塞状态。一般情况下如果一个方法传递了该异常的话,那么这个方法也会变成在阻塞的方法。而那些不能抛出这类异常的代码就必须捕获异常并调用interupt方法在这样方法就会被中断。而通常中断机制是一种协作机制,例如当一个线程中断另一个线程是并不是这个线程粗暴的打断另一个线程的运行而是发出要求,当运行的线程到达某个状态时可以停止目前的操作,但是如果运行的线程不愿意那么你也没办法O(∩_∩)O哈哈哈~,但是如果你运气好碰到了一个对中断请求响应很高的线程那么它既可以停下来了,ヾ(。`Д´。)。
5同步工具类
同步工具类除了上述的阻塞队列之外还包括信号量,栅栏闭锁等。
①其中闭锁相当于一扇门,在所有线程到达之前折扇们是关闭的,当所有线程就绪后,这扇门将被打开,例如游戏中玩家的连接就是如此,其中countdownlatch就是一个很典型的闭锁,该类的内并不有一个计数器当这个计数器为需要等待的线程,如果计数器的值部位0,那么线程将一直阻塞。第二种闭锁是FutureTask,FutureTask表示的计算结果通常有Callable实现。
②信号量控制同时访问某个资源的数量,或者同事进行某种操作的数量。Semaphore中管理着一组虚拟的许可,某个线程执行某个特定的操作或者访问某个特定的资源时会消耗一个许可,操作完成时返回一个许可,当许可为0时,将发生阻塞。典型的应用如数据库的连接池
③栅栏
栅栏与闭锁类似不同的是所有线程必须同时到达栅栏位置才能继续执行。闭锁用于等待事件,而栅栏用于等待线程。典型的类如CyclicBarrier
6构建高效可伸缩的缓存
在这部分中作者利用上面利用缓存改进了一个奥计算开销的任务,有兴趣的同学可以阅读原书该部分内容,由于牵扯到的知识在上面已经罗列并且该部分更偏向于事件因此不做总结。