Java Concurrency in Practice 4.1-4.2相关问题及理解

今天终于又从新拿起了Java Concurrency in Practice,以前被虐的体无完肤,在看这本书以前,有一部分本身写的代码我根本没意识到是线程不安全的,还真的是要恶补这方面的知识。安全

1.Java监视器模式

监视器模式其实很简单,就是用私有对象的锁或者内置锁来保证所属对象的线程安全性。这里引入一个例子:车辆追踪性能

public class MonitorVehicleTracker {
    private final Map<String, MutablePoint> locations;
    
    public MonitorVehicleTracker(Map<String, MutablePoint> locations) {
        this.locations = locations;
    }
    
    public synchronized Map<String, MutablePoint> getLocations() {
        return deepCopy(locations);
    }

    public synchronized MutablePoint getLocation(String id) {
        MutablePoint loc = locations.get(id);
        return loc == null ? null : new MutablePoint(loc);
    }
    
    public void setLocation(String id, int x, int y) {
        MutablePoint loc = locations.get(id);
        if(loc == null)
            throw new IllegalArgumentException("No such ID: " + id);
        loc.x = x;
        loc.y = y;
    }

    private static Map<String, MutablePoint> deepCopy(Map<String, MutablePoint> m) {
        Map<String, MutablePoint> result = new HashMap<>();
        for(String id : m.keySet())
            result.put(id, new MutablePoint(m.get(id)));
        return Collections.unmodifiableMap(result);
}

这里MutablePoint是个可变类,是线程不安全的:this

public class MutablePoint {
    public int x, y;
    
    public MutablePoint() { x = 0; y = 0; }
    public MutablePoint(MutablePoint p) {
        this.x = p.x;
        this.y = p.y;
    }
}

细心的读者确定发现了,MonitorVehicleTracker类中几乎每个方法都要复制MutablePoint或者locations的值,缘由在于,这俩不是线程安全的,不能直接发布出去,因此只能发布一个副本,可是这又出了新的问题:虽然MonitorVehicleTracker类是线程安全的,可是因为数据都是复制的,那么假设线程A调用了getLocations()方法获得了位置,此时车的位置变化,线程B调用setLocation()修改了内部变量locations,这时车辆的位置已经修改了,可是线程A返回的仍是旧的位置。固然,若是为了保持数据的一致性,那么这样作就是优势;但若是想要获得车辆的实时位置,就不得不获得车辆位置的最新快照,上述的方法会形成严重的性能问题。那么如何改进呢?在这里我给个提示:复制数据的缘由是由于属性是线程不安全的,不能直接发布,那么,若是发布一个线程安全的属性,是否就解决了实时性的问题?spa

相关文章
相关标签/搜索