当前位置: 首页 > article >正文

Java多线程——对象的组合

设计线程安全的类

  • 找出构成对象状态的所有变量
  • 找出约束状态变量的不变性条件
  • 建立对象状态的并发访问管理策略

实例封闭

当一个对象被封装到另一个对象中,能够访问被封装对象的所有代码路径都是已知的。

通过封闭和加锁,可以确保以线程安全的方式使用非线程安全的对象

class MutablePoint {
    public int x, y;

    public MutablePoint() {
        x = 0;
        y = 0;
    }

    public MutablePoint(MutablePoint p) {
        this.x = p.x;
        this.y = p.y;
    }
}

将MutablePoint封闭到MonitorVehicleTracker,返回对象通过拷贝生成新的对象,并对get和set加锁

class MonitorVehicleTracker {
    @GuardedBy("this")
    private final Map<String, MutablePoint> locations;

    public MonitorVehicleTracker(Map<String, MutablePoint> locations) {
        this.locations = deepCopy(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 synchronized 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<String, MutablePoint>();
        for (String id : m.keySet())
            result.put(id, new MutablePoint(m.get(id)));
        return Collections.unmodifiableMap(result);
    }
}

但数据量大时,每次拷贝会降低性能

线程安全性的委托

将域声明为final变成不可变对象

class Point {
    public final int x, y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

将线程安全委托给ConcurrentMap,返回实时更新且只读的对象引用

class DelegatingVehicleTracker {
    private final ConcurrentMap<String, Point> locations;
    private final Map<String, Point> unmodifiableMap;

    public DelegatingVehicleTracker(Map<String, Point> points) {
        locations = new ConcurrentHashMap<String, Point>(points);
        unmodifiableMap = Collections.unmodifiableMap(locations);
    }

    public Map<String, Point> getLocations() {
        return unmodifiableMap;
    }

    public Point getLocation(String id) {
        return locations.get(id);
    }

    public void setLocation(String id, int x, int y) {
        if (locations.replace(id, new Point(x, y)) == null)
            throw new IllegalArgumentException(
                    "invalid vehicle name:" + id);
    }
}

若需要非实时更新且只读的对象引用,因为map内容是不可变的,可以浅拷贝

public Map<String, Point> getLocations() {
    return Collections.unmodifiableMap(new HashMap<>(locations));
}

若想要可变且线程安全的Point类,需要注意get应该同时获得x和y,若分别提供的话,可能会在两次调用中导致状态不一致

class SafePoint {
    @GuardedBy("this")
    private int x, y;

    private SafePoint(int[] a) {
        this(a[0], a[1]);
    }

    public SafePoint(SafePoint p) {
        this(p.get());
    }

    public SafePoint(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public synchronized int[] get() {
        return new int[]{x, y};
    }

    public synchronized void set(int x, int y) {
        this.x = x;
        this.y = y;
    }
}
class PublishingVehicleTracker {
    private final Map<String, SafePoint> locations;
    private final Map<String, SafePoint> unmodifiableMap;

    public PublishingVehicleTracker(Map<String, SafePoint> locations) {
        this.locations = new ConcurrentHashMap<String, SafePoint>(locations);
        this.unmodifiableMap = Collections.unmodifiableMap(this.locations);
    }

    public Map<String, SafePoint> getLocations() {
        return unmodifiableMap;
    }

    public SafePoint getLocation(String id) {
        return locations.get(id);
    }

    public void setLocation(String id, int x, int y) {
        if (!locations.containsKey(id))
            throw new IllegalArgumentException(
                    "invalid vehicle name:" + id);
        locations.get(id).set(x, y);
    }
}

在现有的线程安全类中添加功能

最简单的方法是修改原始类,若没有源代码通过扩展实现,如下实现putIfAbsent()

class BetterVector<E> extends Vector<E> {
    public synchronized boolean putIfAbsent(E x) {
        boolean absent = !contains(x);
        if (absent)
            add(x);
        return absent;
    }
}

但如果底层的类改变了同步策略并选择了不同的锁来保护它的状态变量,那么子类会被破坏

客户端加锁机制

客户端加锁是指,对于使用某个对象X的客户端代码,使用X本身用于保护其状态的锁来保护这段客户代码

对于由Collections.synchronizedList封装的ArrayList等类,可通过辅助类

class ListHelper<E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<>());

    public boolean putIfAbsent(E x) {
        boolean absent = !list.contains(x);
        if (absent)
            list.add(x);
        return absent;
    }
}

但上面的做法是错误的,因为synchronizedList内部的锁和ListHelper的锁不是同一个

class ListHelper<E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<>());

    public boolean putIfAbsent(E x) {
        synchronized (list) {
            boolean absent = !list.contains(x);
            if (absent)
                list.add(x);
            return absent;
        }
    }
}

但会将类的加锁代码放到与其完全无关的其他类中,会破坏同步策略的封装性

组合

将操作委托给底层的list,并新增加锁方法

class ImprovedList<T> implements List<T> {

    private final List<T> list;

    public ImprovedList(List<T> list) {
        this.list = list;
    }

    public synchronized boolean putIfAbsent(T x) {
        boolean absent = !list.contains(x);
        if (absent)
            list.add(x);
        return absent;
    }
	//......
}

通过自身的内置锁增加了一层额外的加锁,即使List不是线程安全的或者修改了它的加锁实现,ImprovedList仍是线程安全的


http://www.kler.cn/a/531258.html

相关文章:

  • 本地快速部署DeepSeek-R1模型——2025新年贺岁
  • 【信息系统项目管理师-选择真题】2021上半年综合知识答案和详解
  • 031.关于后续更新和指纹浏览器成品
  • K8S集群部署--亲测好用
  • 强化学习、深度学习、深度强化学习的区别是什么?
  • 21款炫酷烟花代码
  • FPGA|例化生成的PLL功能IP核
  • 为什么在Rust中要用Struct和Enum组织数据?
  • MySQL锁类型(详解)
  • React+Cesium基础教程(003):加载3D建筑物和创建标签
  • 利用deepseek参与软件测试 基本架构如何 又该在什么环节接入deepseek
  • Pathformer3D: A 3D Scanpath Transformer for 360° Images(浅看)
  • Simula语言的物联网
  • pycharm 中的 Mark Directory As 的作用是什么?
  • C++11新特性之constexpr
  • Machine Learning Engineering Open Book 机器学习工程开放书
  • 八、Spring Boot 日志详解
  • 带负权值的图如何计算最短路径?
  • w190工作流程管理系统设计与实现
  • Nginx反向代理 笔记250203
  • web-SQL注入-CTFHub
  • 55【ip+dns+域名关系】
  • 说说 Java 中 HashMap 的原理?
  • 51单片机看门狗系统
  • 测试方案和测试计划相同点和不同点
  • 路径规划之启发式算法之二十九:鸽群算法(Pigeon-inspired Optimization, PIO)