zoukankan      html  css  js  c++  java
  • 第三部分-并发设计模式29:Copy-on-write

    1.Copy-on-Write

    又称COW,写时复制
    String的replace()方法,没有修改内部的value数组,而是新创建了一个不可变对象
    这种方法在解决不可变对象时,经常使用
    这其实就是一种Copy-on-write方式
    不可变对象的写操作往往都是使用 Copy-on-Write 方法解决的,当然 Copy-on-Write 的应用领域并不局限于 Immutability 模式

    2.CopyOnWriteArrayList 和 CopyOnWriteArraySet

    本质就是使用了COW思想
    线程安全,试用于读多写少的场景,提升了性能的同时,也是通过COW复制出新的内存空间的代价换来的
    读无锁,性能高。
    修改的时候,会同事复制整个容器,以复制内存为代价,提升性能

    3.linux的copy-on-write

    fork子进程的时候,也是使用这种思路
    父进程的地址空间现在用到了 1G 的内存,那么 fork() 子进程的时候要复制父进程整个进程的地址空间(占有 1G 内存)给子进程,这个过程是很耗时的。而 Linux 中的 fork() 函数就聪明得多了,fork() 子进程的时候,并不复制整个进程的地址空间,而是让父子进程共享同一个地址空间;只用在父进程或者子进程需要写入的时候才会复制地址空间,从而使父子进程拥有各自的地址空间。
    在进程方面,因为子进程和父进程必须是不同的进程地址空间,数据也要隔离,所以copy-on-write其实只是一种延迟策略,在需要时复制,不需要时使用父进程的地址空间

    4.微服务路由表应用案例

    客户端调用远程服务,需要获取路由表,然后从路由表选则其中之一去调用
    每次调用前,都需要获取路由表,服务的上下线状态不是很频繁,而且一段时间间隔非实时性也是可以容忍的,弱一致性

    思考:
    服务提供方,每次上线,下线,都会更新路由信息。
    是否可以通过更新Router的一个状态位来表示,如果这样,那么客户端获取路由,访问该状态位的时候都需要同步访问(有可能获取状态的时候,状态正在被修改,假如被加锁,这时候就需要等待,等待写完了,再读),性能不好。如果采用Immutability不可变模式,每次上线,下线,都创建新的Router对象或者删除对应的Router对象,直接访问不受影响
    代码范例

    
    //路由信息
    public final class Router{
      private final String  ip;
      private final Integer port;
      private final String  iface;
      //构造函数
      public Router(String ip, 
          Integer port, String iface){
        this.ip = ip;
        this.port = port;
        this.iface = iface;
      }
      //重写equals方法
      public boolean equals(Object obj){
        if (obj instanceof Router) {
          Router r = (Router)obj;
          return iface.equals(r.iface) &&
                 ip.equals(r.ip) &&
                 port.equals(r.port);
        }
        return false;
      }
      public int hashCode() {
        //省略hashCode相关代码
      }
    }
    //路由表信息
    public class RouterTable {
      //Key:接口名
      //Value:路由集合
      ConcurrentHashMap<String, CopyOnWriteArraySet<Router>> 
        rt = new ConcurrentHashMap<>();
      //根据接口名获取路由表
      public Set<Router> get(String iface){
        return rt.get(iface);
      }
      //删除路由
      public void remove(Router router) {
        Set<Router> set=rt.get(router.iface);
        if (set != null) {
          set.remove(router);
        }
      }
      //增加路由
      public void add(Router router) {
        Set<Router> set = rt.computeIfAbsent(
          route.iface, r -> 
            new CopyOnWriteArraySet<>());
        set.add(router);
      }
    }
    
    原创:做时间的朋友
  • 相关阅读:
    leetcode 850. Rectangle Area II
    leetcode 699. Falling Squares 线段树的实现
    leetcode 847. Shortest Path Visiting All Nodes 无向连通图遍历最短路径
    leetcode 843. Guess the Word
    javaMail实现收发邮件(三)
    javaMail实现收发邮件(二)
    javaMail实现收发邮件(一)
    springboot整合websocket实现一对一消息推送和广播消息推送
    jieba分词/jieba-analysis(java版)
    java实现两个不同list对象合并后并排序
  • 原文地址:https://www.cnblogs.com/PythonOrg/p/14846428.html
Copyright © 2011-2022 走看看