Java并发编程学习记录#4

组合对象

探讨一些构造类的模式,使得类更容易成为线程安全的。

设计线程安全的类

设计线程安全的类的过程应该包含三个方面:
- 确定对象状态是由哪些变量构成–变量;
- 确定限制对象状态的不变约束–不变约束;
- 制定一个管理并发访问对象状态的策略–后验条件。

不变约束:用来判定一个状态是合法的还是不合法的,比如int的取值范围,是施加在状态上的约束;

后验条件:指出某种状态转变是否合法,是施加在状态操作上的约束;

上述二者需要引入额外的同步和封装。

组合:将一个对象封装到另一个对象内部。

组合使得被封装对象的全部访问路径都是可知的,这相比让整个应用系统 访问对象来说,更容易对访问路径进行分析,然后再和各种适当的锁相结合,可以确保程序能以线程安全的方式使用其它非线程安全的对象。

在并发领域,组合是为保证线程安全的的一个线程限制。

Java监视器模式

一种线程限制原则,遵循该原则的对象封装了所有的可变状态,并使用对象的内部锁来保护。例如:

public class PrivateLock {
    private final Object myLock = new Object();
    @GuardedBy("myLock") Widget widget;

    void someMethod() {
        synchronized (myLock) {
            // Access or modify the state of widget
        }
    }
}

委托线程安全

一些由线程安全的组件组合而成的组件未必是线程安全的。比如这些线程安全的子组件有依赖性。见代码:

public class NumberRange {
    // INVARIANT: lower <= upper
    private final AtomicInteger lower = new AtomicInteger(0);
    private final AtomicInteger upper = new AtomicInteger(0);

    public void setLower(int i) {
        // Warning -- unsafe check-then-act
        if (i > upper.get())
            throw new IllegalArgumentException("can't set lower to " + i + " > upper");
        lower.set(i);
    }

    public void setUpper(int i) {
        // Warning -- unsafe check-then-act
        if (i < lower.get())
            throw new IllegalArgumentException("can't set upper to " + i + " < lower");
        upper.set(i);
    }

    public boolean isInRange(int i) {
        return (i >= lower.get() && i <= upper.get());
    }
}

若有两个线程,同时,分别访问setLower和setUpper,则可能出现线程安全问题。

只有当一个类有多个彼此独立的 线程安全的状态变量组合而成,且类的操作不包含任何无效的状态转换时,才可以将线程安全委托给这些状态变量。

向已有的线程安全类中添加功能

重用Java自带的线程安全类,要好于创建一个新的,无论在难度,风险或是维护上。比如对一个List添加功能:缺少则添加。即先判断list中是否有此元素,无,则添加。此时涉及到了检查-执行这一复合操作,按照之前同步策略,是可以在此操作上加锁将其变成原子性操作的,但是因为源码我们没法修改,只能找别的方式。这里,组合,或是继承都可以。比如继承:

@ThreadSafe
public class BetterVector <E> extends Vector<E> {
    // When extending a serializable class, you should redefine serialVersionUID
    static final long serialVersionUID = -3963416950630760754L;

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

不过组合(这里只指原功能的线程安全委托给子组件)的话,需要一些其它的操作,不能直接在方法上同步。因为Q1操作无法保证helper类封装的list的其它方法和putif..的同步问题。

@NotThreadSafe//Q1操作,不安全
class BadListHelper <E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());

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

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

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

还有另一种组合方式,就是将所组合的对象中所有存在风险的方法都加上内部锁,而不用依赖子组件对象是否线程安全。

@ThreadSafe
public 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 contains = list.contains(x);
        if (contains)
            list.add(x);
        return !contains;
    }

    public synchronized boolean add(T e) {
        return list.add(e);
    }

    public synchronized boolean remove(Object o) {
        return list.remove(o);
    }
    .....other alike methods..

写好文档,非常重要!

//待下篇

主要参考自_ Java Concurrency in Practice
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
嗨!很高兴回答你关于Java并发编程的问题。请问你想知道什么方面的内容呢?我可以分享一些学习笔记和建议给你。 1. 并发编程基础:了解并发编程的基本概念,如线程、进程、锁、同步等。学习Java中的并发编程模型以及相关的API,如Thread、Runnable、Lock、Condition等。 2. 线程安全性:学习如何保证多线程环境下的数据安全性,了解共享资源的问题以及如何使用同步机制来防止数据竞争和并发问题。 3. 线程间的通信:掌握线程间的通信方式,如使用wait/notify机制、Lock/Condition等来实现线程的协调与通信。 4. 并发容器:学习并发容器的使用,如ConcurrentHashMap、ConcurrentLinkedQueue等。了解它们的实现原理以及在多线程环境下的性能特点。 5. 并发工具类:熟悉Java提供的并发工具类,如CountDownLatch、CyclicBarrier、Semaphore等,它们可以帮助你更方便地实现线程间的协作。 6. 并发编程模式:学习一些常见的并发编程模式,如生产者-消费者模式、读者-写者模式、线程池模式等。了解这些模式的应用场景和实现方式。 7. 性能优化与调试:学习如何分析和调试多线程程序的性能问题,了解一些性能优化的技巧和工具,如使用线程池、减少锁竞争、避免死锁等。 这些只是一些基本的学习笔记和建议,Java并发编程是一个庞大而复杂的领域,需要不断的实践和深入学习才能掌握。希望对你有所帮助!如果你有更具体的问题,欢迎继续提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值