并发编程---写入时复制COW

写入时复制(copy on write)

作为使用频率很高的List和Set,JUC提供了相应的线程安全集合,就是Copy-On-Write。分别为CopyOnWriteArrayList和CopyOnWriteArraySet。

1、应用场景

Copy-On-Write并发容器用于读多写少的并发场景。

  • 比如Nacos的注册中心同步方式,用的是copyonwrite,就是复制一份副本,写入后直接替换。注册中心的实时性并没有那么高,所以使用cow来保证数据的最终一致性即可。

  • Redis的COW

    • Redis在持久化时,如果是采用BGSAVE命令或者BGREWRITEAOF的方式,那Redis会fork出一个子进程来读取数据,从而写到磁盘中
    • 总体来看,Redis还是读操作比较多。如果子进程存在期间,发生了大量的写操作,那可能就会出现很多的分页错误(页异常中断page-fault),这样就得耗费不少性能在复制上。
    • 而在rehash阶段上,写操作是无法避免的。所以Redis在fork出子进程之后,将负载因子阈值提高,尽量减少写操作,避免不必要的内存写入操作,最大限度地节约内存。
  • 实际运用:一个充值排行榜的功能,排行榜会有很多人查看访问,但是只有充值之后才会修改排行榜上的数据,或者充值之后也不更新,只有每天晚上9点更新排行榜,标准的读多写少。

public class CopyOnWriteArrayListTest {
	public static CopyOnWriteArrayList<Integer> rankIds = new CopyOnWriteArrayList<Integer>(); 
	public static void addRankIds(int id) { 
        /* * 获取id在rankIds中的排序,代码省略 * 假设id应该在排行榜中的第一个 */ 
        rankIds.add(0, id); 
	}
}

2、定义

写入时复制(英语:Copy-on-write,简称COW)是一种计算机程序设计领域的优化策略。

3、原理

核心思想,如果有多个调用者(callers)同时要求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。这过程对其他的调用者都是透明的(transparently)。此作法主要的优点是如果调用者没有修改该资源,就不会有副本(private copy)被创建,因此多个调用者只是读取操作时可以共享同一份资源。

  • 当读取共享数据时,直接读取,不需要有其他操作(比如阻塞等待、复制等)。读数据不会被阻塞

    get()方法是没有加锁的

    public E get(int index) {
        return get(getArray(), index);
    }
    final Object[] getArray() { 
        return array;
    }
    private E get(Object[] a, int index) {
        return (E) a[index];
    }
    
  • 当写共享数据时,将旧数据复制出来一份作为新数据,只修改新数据,修改完新数据之后将新数据的引用赋值给原来数据的引用。在整个写数据的过程中,所有读取共享数据的操作都是读的旧数据。

    • lock锁同步

    • 旧数组复制出一个新数组

    • 新数组添加元素

    • 新数组引用赋给array

    **add()**方法

    public boolean add(E e) {
    	final ReentrantLock lock = this.lock; lock.lock();
    	// 1. lock锁同步 
    	try { 
            // 2. 旧数组复制出一个新数组 
            Object[] elements = getArray(); 
            int len = elements.length; 
            Object[] newElements = Arrays.copyOf(elements, len + 1); 
            // 3. 新数组添加元素 
            newElements[len] = e; 
            // 4. 新数组引用赋给
            array setArray(newElements); 
            return true; 
    	} finally { 
    		lock.unlock();// 解锁 
    	}
    }
    
  • COW容器只有写操作与写操作之间是互斥的,读读和读写都不互斥。

4、并发思维导图

img

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值