在多线程环境中,为了保证共享数据的一致性,每每须要对共享数据的使用进行加锁,可是加锁操做自己就会带来必定的开销,这里可使用将共享数据使用不可变对象进行封装,从而避免加锁操做。java
不可变对象指的是,对象内部没有提供任何可供修改对象数据的方法,若是须要修改共享变量的任何数据,都须要先构建整个共享对象,而后对共享对象进行总体的替换,经过这种方式来达到对共享对象数据一致性的保证。以下是不可变对象设计的类图:git
以下是各个角色功能的描述:多线程
对于不可变对象,其主要有以下三种使用场景:this
对于不可变对象,一个很好的例子就是地址经纬度。笔者所工做的公司处理的业务和房源相关,其中有一部分就是须要处理房源所在点的经纬度信息,这里就可使用不可变对象,由于房源经纬度基本上不会发生变化,而且对其操做也主要是以查询为主,最重要的是,对经纬度的处理必须是经度和纬度同时发生变化,任何状况下只更改了其中一个数据都会产生问题。以下是记录房源经纬度的类:线程
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的键或值任何一方可能发生变化,那么在返回值则必须返回一个深度复制的结果,这样才能保证原始数据的完整性。code