CopyOnWrite容器

/*
 * ## CopyOnWrite容器
 * 
 * CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,
 * 不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的
 * 容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是
 * 我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任
 * 何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。 
 * 
 * 具体的实现类:
 *     CopyOnWriteArrayList和CopyOnWriteArraySet(具体实现为CopyOnWriteArrayList)
 * 
 * ## CopyOnWriteArrayList的实现原理
 * 
 * 可以发现在添加的时候是需要加锁的,否则多线程写的时候会Copy出N个副本出来。读的时候
 * 不需要加锁,如果读的时候有多个线程正在向ArrayList添加数据,读还是会读到旧的数据,
 * 因为写的时候不会锁住旧的ArrayList
 * 
 * CopyOnWriteArrayList每次添加元素都是在原始数组的长度基础上+1,删除元素则长度-1
 * 
 * ### CopyOnWrite的应用场景
 * CopyOnWrite并发容器用于读多写少的并发场景。
 * 
 * 比如白名单,黑名单,商品类目的访问和更新场景,假如我们有一个搜索网站,用户在这个网
 * 站的搜索框中,输入关键字搜索内容,但是某些关键字不允许被搜索。这些不能被搜索的关键
 * 字会被放在一个黑名单当中,黑名单每天晚上更新一次。当用户搜索时,会检查当前关键字在
 * 不在黑名单当中,如果在,则提示不能搜索
 * 
 * 注意两点:
 * - 减少扩容开销。根据实际需要,初始化一个Set,将数据全局添加到set中,然后再使用
 * addAll之类的方法一行添加集合中的所有元素,以避免每次写时扩容的开销,合理的评估
 * - 使用批量添加。因为每次添加,容器每次都会进行复制,所以减少添加次数,可以减少容器
 * 的复制次数。

### CopyOnWrite的缺点
    1、内存占用问题。因为CopyOnWrite的写时复制机制,所以在进行写操作的时候,内存里会
    同时驻扎两个对象的内存,旧的对象和新写入的对象(注意:在复制的时候只是复制容器里的
    引用,只是在写的时候会创建新对象添加到新容器里,而旧容器的对象还在使用,所以有两份
    对象内存)。
    
    针对内存占用问题,可通过压缩容器中的元素的方法来减少大对象的内存消耗,如果元素全是
    10进制的数字,可以考虑把它压缩成36进制或64进制。或者不使用CopyOnWrite容器,而使
    用其他的并发容器,如ConcurrentHashMap。
    
    2、数据一致性问题。CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一
    致性。所以如果你希望写入的的数据,马上能读到,请不要使用CopyOnWrite容器。
    
### ConcurrentSkipListMap
    ConcurrentSkipListSet
    
        二分查找要求元素可以随机访问,所以决定了需要把元素存储在连续内存。这样查找确实很快,但是插入和删除元素的时候,为了保证元素的有序性,就需要大量的移动元素了。
如果需要的是一个能够进行二分查找,又能快速添加和删除元素的数据结构,首先就是二叉查找树,二叉查找树在最坏情况下可能变成一个链表。
于是,就出现了平衡二叉树,根据平衡算法的不同有AVL树,B-Tree,B+Tree,红黑树等,但是AVL树实现起来比较复杂,平衡操作较难理解,这时候就可以用SkipList跳跃表结构。

## monitor

    Monitor可以翻译成监视器或者管程,是由JVM提供的,当线程执行到对象方法的临界区时,首先去看对象头是
不是关联了Monitor,如果关联了加入Monitor的EntryList中,没有关联就改变对象的对象头的Mark Word或者指
向一个Monitor,然后Monitor的拥有者就变成了该线程,执行完了之后,Monitor再把线程释放,去EntryList找
一个新的拥有者(非公平竞争,不是先来的就先得)

- 一个对象关联一个Monitor;当且一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter指令时
,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁
- Java虚拟机给每个对象和class字节码都设置了一个监听器Monitor,用于检测并发代码的重入,同时在
Object类中还提供了notify和wait方法来对线程进行控制
- 重量级锁也就是通常说synchronized的对象锁,锁标识位为10,其中指针指向的是monitor对象(也称为管程
或监视器锁)的起始地址
- 不加synchronized的对象不会关联监视器;
- 自旋优化:在竞争锁的时候,自旋重试几次,要是此时锁被释放就进入,否则进入阻塞状态

java中每个对象都有唯一的一个monitor,想拥有一个对象的monitor的话有以下三种方式:
1、执行该对象的同步方法`public synchronize a () {}`
2、执行该对象的同步块synchronize(obj) {}`
3、执行某个类的静态同步方法`public static synchronize b(){}`。

Monitor可以类比为一个特殊的房间,这个房间中有一些被保护的数据,Monitor保证每次只能有一个线程能进入这个房间进行访问被保护的数据,进入房间即为持有Monitor,退出房间即为释放Monitor。

当一个线程需要访问受保护的数据(即需要获取对象的Monitor)时,它会首先在entry-set入口队列中排队(这里并不是真正的按照排队顺序),如果没有其他线程正在持有对象的Monitor,那么它会和entry-set队列和wait-set队列中的被唤醒的其他线程进行竞争(即通过CPU调度),选出一个线程来获取对象的Monitor,执行受保护的代码段,执行完毕后释放Monitor,如果已经有线程持有对象的Monitor,那么需要等待其释放Monitor后再进行竞争。
 
 ### 容错机制

failfast快速失败

- 如果出现故障,则立即报错。
- 通常用于非幂等性操作,如:下单操作,如果写入故障,则立即报错,不必重试

failsafe故障安全

- 如果出现故障,则可以忽略,因为这种故障不会造成损失或损失在可接受范围内。
- 通常用于辅助性操作,如:写入监控日志,如果写入故障,则忽略。

failover故障切换

- 如果出现故障,则重试备份操作模式。
- 通常用于幂等性操作,如:MySql的双Master模式,如果主Master故障,则切换至从Master。
- 重试通常会带来更多延时。

failback故障恢复

- 故障切换之后,如果主要操作模式恢复,则自动从备份操作模式恢复主要操作模式。
- 如:MySql的双Master模式,如果主Master故障,则failover至从Master;当主Master恢复之
后,则自动切换至主Master。
 *
 */

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值