程序员多线程

Java多线程编程之不可变对象模式

2018-05-13  本文已影响42人  charming_coder

       在多线程环境中,为了保证共享数据的一致性,往往需要对共享数据的使用进行加锁,但是加锁操作本身就会带来一定的开销,这里可以使用将共享数据使用不可变对象进行封装,从而避免加锁操作。

1. 模型角色

       不可变对象指的是,对象内部没有提供任何可供修改对象数据的方法,如果需要修改共享变量的任何数据,都需要先构建整个共享对象,然后对共享对象进行整体的替换,通过这种方式来达到对共享对象数据一致性的保证。如下是不可变对象设计的类图:

不可变对象类图

如下是各个角色功能的描述:

2. 使用场景

       对于不可变对象,其主要有如下三种使用场景:

3. 使用示例

       对于不可变对象,一个很好的例子就是地址经纬度。笔者所工作的公司处理的业务和房源相关,其中有一部分就是需要处理房源所在点的经纬度信息,这里就可以使用不可变对象,因为房源经纬度基本上不会发生变化,并且对其操作也主要是以查询为主,最重要的是,对经纬度的处理必须是经度和纬度同时发生变化,任何情况下只更改了其中一个数据都会产生问题。如下是记录房源经纬度的类:

public final class Location {
  private final long id;
  private final String latitude;
  private final String longitude;

  public Location(long id, String latitude, String longitude) {
    this.id = id;
    this.latitude = latitude;
    this.longitude = longitude;
  }

  public long getId() {
    return id;
  }

  public String getLatitude() {
    return latitude;
  }

  public String getLongitude() {
    return longitude;
  }
}

       该Location类也即上述UML类图中的ImmutableObject部分。可以看到,任何对Location对象的修改都必须重新构建一个Location对象。如下是对Location的管理类,用于存储具体的Location信息的:

public class LocationHolder {
  
  private final LocationHolder INSTANCE = new LocationHolder();
  private Map<Long, Location> locations;

  private LocationHolder() {
    this.locations = new ConcurrentHashMap<>();
  }
  
  public LocationHolder getInstance() {
    return INSTANCE;
  }

  public Location getLocation(long id) {
    return locations.get(id);
  }

  public void addLocation(long id, String latitude, String longitude) {
    Location location = new Location(id, latitude, longitude);
    locations.put(id, location);
  }
  
  public Map<Long, Location> getLocations() {
    return Collections.unmodifiableMap(locations);
  }
}

        可以看到,这里对Location的管理是通过一个单例类LocationHolder进行的,任何对Location的操作都进行了封装,并且这里批量获取Location,也是返回了一个不可变Map,从而保证原始数据不会作任何修改,如果该Map的键或值任何一方可能发生变化,那么在返回值则必须返回一个深度复制的结果,这样才能保证原始数据的完整性。

上一篇下一篇

猜你喜欢

热点阅读