《Java并发编程实战》第4、5章学习笔记

本周进行了《Java并发编程实战》第4、5章的学习,这部分的知识更加侧重于并发编程的思想。

对象的组合
  • 对象的状态:由对象中的所有所有域以及域中所有被引用对象的域组成。

  • 状态空间:即对象中所有状态的可能的取值。状态空间越小,就越容易判断线程的状态。其中final类型的域的状态只有唯一的一个,所以尽量使用final类型的变量。

  • 不可变条件:用来判断状态的变化是否是有效的,也就是在多线程环境中对变量的各种条件约束。例如:在多线程环境中,变量的变化不能小于0且不能大于100,修改变量前需要参考之前的值大小。

    在设计线程安全类的过程中,需要包含三个基本要素:
    1. 找出构成对象状态的所有变量
    2. 找出约束状态变量的不变性条件
    3. 建立对象状态的并发访问管理策略(同步策略)
    
  • 对象状态的所有权与封装性是互相关联的:对象封装它所拥有的状态,因此对象对它封装的状态拥有所有权。其中,容器类会体现出 “所有权分离“ 的形式。

    例如:ServletContext对象是线程安全的,但是它所保管的对象是在客户程序中进行使用,所以,这些被保管的对象要么是线程安全的对象,要么是事实不可变的对象才能保证多个线程在并发访问同一个对象时不会互相干扰。

  • 将数据封装在对象内部,并将数据的访问限制在对象的方法上,更容易确保线程在访问数据时持有正确的锁,也更易于对代码进行分析和排查锁是否被正确的使用。

  • 实例封闭使得在锁策略的选择上拥有更多的灵活性,可以使得不同的状态变量由不同的锁来进行访问限制和保护。

  • Java监视器模式使用私有的锁对象而不是使用内置锁的原因在于,客户程序可以通过公有方法得到对象的私有锁,以便客户代码可以正确的参与到该对象的同步策略中。

    // Java使用私有锁的监视器模式
    publi class MonitorSchemaDemo{
        private final Object lock = new Objcet();
        private Object obj;
        
        public void doSomething(){
            synchronized(lock){
                // 修改对象中变量的状态
            }
        }
    }
    
  • 线程安全性的委托:之所以对象是线程安全的,是因为该对象的状态变量是线程安全的,所以对象的线程安全性委托给了这些安全的状态变量。尽量让现有的线程安全类管理所有的状态。

  • 当委托失效时,这个类必须提供自己的加锁机制以保证对状态变量的复合操作是原子操作。

  • 在现有的安全类中添加功能可以使用组合(Composition)的方法,如下示例:

    public class SafeList<T> implements List<T>{ // 使用监视器模式封装外部的list
        private final List<T> list;
        
        // 将外部的list委托给内部底层的list对象
        public SafeList(List<T> list){ this.list = list; } 
        
        public synchronized boolean putIfAbsent(T t){
            boolean contains = list.contains(t);
            if(contains){
                list.add(t);
            }
            return !contains;
        }
        
        // 将所有的方法实现为synchronized
    }
    
基础构建模块
  • 容器在迭代过程中被修改时,会抛出一个ConcurrentModificationException异常。包括在使用Iterator迭代和forEach迭代的时候。

  • 普通的容器类在调用toString方法时会迭代容器里的元素调用toString方法,会有隐藏的迭代器调用,在并发环境下可能会引发ConcurrentModificationException异常。

  • 普通容器的hashCode和equals方法也会间接的执行迭代操作。

  • ConcurrentHashMap在迭代过程中可以进行并发修改,并可以在迭代器被构造后将修改操作反映给容器,但是只是具有弱一致性,并不能保证会同步修改操作。

  • ConcurrentHashMap在同步时使用的是分段锁,即在操作元素时,同步的锁对象是对应的key值对象,这样锁的粒度会比在整个方法上加同步关键字要细。

  • ConcurrentHashMap的size方法和isEmpty方法返回的是一个近似值,因为在并发环境下这两个方法使用场景不多,已被弱化。

  • BlockingQueue实现生产者-消费者模式代码见上周的学习笔记。

  • SynchronousQueue内部维护的是一组线程,并没有为队列中元素维护的存储空间。put和take操作会一直阻塞,直到有另一个线程已经准备好参与到交付过程中。

  • 串行线程封闭:线程封闭是单个线程拥有该对象的所有权,但可以通过安全的发布该对象 ”转移“ 所有权到另一个线程中,并且发布对象的线程不会再使用该对象。(使用公共队列的消费者-生产者模式)

  • 在Runnable的代码中,必须捕获InterruptedException异常,并通过调用当前线程的interrupt方法引发一个中断。

    public class Task implements Runnable{
        public void run(){
            try{
                // InterruptedException
            }catch(InterruptedExceptoin e){
                Thread.currentThread().interrupt();
            }
        }
    }
    
  • 闭锁与栅栏的区别:所有线程必须同时到达栅栏位置,才能继续执行;闭锁用于等待事件,栅栏用于等待其他线程。两者都能阻塞一组线程指导某个事件发生。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值