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是个可变类,是线程不安全的:

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返回的还是旧的位置。当然,如果为了保持数据的一致性,那么这样做就是优点;但如果想要得到车辆的实时位置,就不得不得到车辆位置的最新快照,

上述的方法会造成严重的性能问题。那么如何改进呢?在这里我给个提示:复制数据的原因是因为属性是线程不安全的,不能直接发布,那么,如果发布一个线程安全的属性,是否就解决了实时性的问题?

猜你喜欢

转载自www.cnblogs.com/cedriccheng/p/9016810.html