【读过的书,留下的迹】Java并发编程实战

前言

  最近发现有时候看完一本书,时间久了容易忘记,看书不总结思考效果大打折扣,故打算写这一系列文章,一是为了整理书中的要点,帮助自己消化理解;二是勉励自己多看书思考。文章中不会把书中内容讲解的非常详细,只是总结概括,适合已经阅读过该书的读者。

第一部分:基础知识

第2章:线程安全性

  • Java中的主要同步机制是关键字synchronized,它提供了一种独占的加锁方式,但同步这个术语还包括volatile类型的变量,显示锁以及原子变量
原子性
  • 可采用原子操作,如AutomicLong、AutomicRefference来避免竞态条件
加锁机制
  • 每个Java对象都可以当作一个同步锁,这些锁被称为内置锁,或监视器锁

  • 内置锁都是可重入的,意味着获取锁的操作的粒度是线程,而不是调用

第3章:对象的共享

可见性
  • 不可见性会导致:实效数据,一个线程更新变量后,可能另一个线程不能看见

  • 加锁的含义不仅仅局限于互斥行为,还包括内存可见性。为了确保所有线程都能看到共享变量的最新值,所有执行读操作或者写操作的线程都必须在同一个锁上同步

  • volatile声明后,编译器与运行时都会注意都这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序

  • 加锁机制既可以确保可见性又可以确保原子性,而volatile只能确保可见性

发布与逸出
  • 发布(Publish):对象能够在当前作用域之外的代码中使用
  • 逸出(Escape):某个不应该发布的对象被发布时,这种情况就被称为逸出
    • ps:如在构造过程中使this引用逸出的,在构造函数中启动一个线程会产生这种逸出
线程封闭
  • 仅在单线程中访问数据,就不需要同步,这种技术称为线程封闭

  • 维持线程封闭性规范的方法是使用ThreadLocal,这个类能使线程中的某个值与保存值的对象关联起来

不可变性
  • 满足同步需求的另一种方法是使用不可变对象,不可变对象一定是线程安全的,需满足一下条件

    • 对象创建以后其状态就不能修改
    • 对象的所有域都是final类型
    • 对象是正确创建的(在创建期间,this引用没有逸出)
  • final类型的域是不能修改的,但如果final域所引用的对象是可变的,那么这些被引用的对象是可以修改的

安全发布
  • 要安全地发布一个对象,对象的引用以及对象的状态必须同时对其他线程可见。一个正确构造的对象可以通过一下方式来安全发布:
    • 在静态初始化函数中初始化一个对象引用
    • 将对象的引用保存在volatile类型或者AtomicReference对象中
    • 将对象的引用保存到某个正确构造对象的final类型域中
    • 将对象的引用保存到一个由锁保护的域中

第4章:对象组合

  • 在现有的线程安全类中添加功能
    • 客户端加锁机制
    • 组合方式

第5章:基础构建模块

同步容器
  • 包括:Vector、HashTable、Conllections.synchronizedXxx等工厂方法创建的
    • 同步容器的迭代器并没有考虑到并发修改的问题,表现出的行为是及时失败(fail-fast)
    • 容器的hashCode、equals、containsAll、removeAll等方法会间接执行迭代操作,并发程序中注意更改容器引起迭代器失效
并发容器
  • ConcurrentHashMap替代Map
  • ConcurrentSkipListMap替代SortedMap
  • ConcurrentSkipListSet替代SortedSet

  • ConcurrentHashMap

    • 采用分段锁
    • 返回的迭代器具有弱一致性,并非及时失败
  • CopyOnWriteArrayList
    • 每次修改都会创建并重新发布一个新的容器副本
    • 仅当迭代操作远远多于修改操作时使用
  • 阻塞队列
    • LinkedBlockingQueue、ArrayBlockingQueue、PriorityBlockingQueue
同步工具
  • 闭锁(Latch)
    • 作用相当于一扇门,在闭锁到达状态之前,一直关着,没有线程能通过,当到达结束状态时,允许所有线程通过
  • FutureTask
    • 可以用做闭锁
  • 信号量(Semaphore)
    • 计数信号量用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的数量
  • 栅栏(Barrier)
    • 所有线程必须同时到达栅栏才能继续执行
    • 闭锁用于等待事件,栅栏用于等待其他线程

结构化并发应用程序

第6章:任务执行

(1)在线程中执行任务

  • 串行执行:效率低
  • 显示创建线程
    • 线程生命周期开销非常高
    • 内存等资源消耗
    • 稳定性不好

(2)Executor框架

  • 线程池:优势:重用现有的线程,在处理多个请求时分摊在线程创建和销毁过程中产生的开销;请求到达时,线程已经存在,不会延迟执行任务

    • Executors.newFixedThreadPool:固定长度的线程池
    • newCachedThreadPool:线程池规模不存在限制,可回收空闲线程,可按需增加新的线程
    • newSingleThreadExecutor:单线程的Executor
  • Executor的生命周期有三个状态(1)运行(2)关闭(3)已终止

    • shutdonw():执行平缓的关闭,不再接受新的任务,等待已经提交的任务执行完成
    • shutdownNow():执行暴力的管理,尝试取消所有运行中的任务
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值