zoukankan      html  css  js  c++  java
  • Java中编写线程安全代码的原理(Java concurrent in practice的快速要点)

    Java concurrent in practice是一本好书,不过太繁冗.本文主要简述第一部分的内容。

    多线程

    优势

    • 与单线程相比,可以利用多核的能力;
    • 可以方便的建模成一个线程处理一种任务;
    • 与异步模型相比,多线程同步模型更简单;
    • 通过分离界面线程和工作线程, 可用于创建灵敏的用户界面.

    劣势

    • 多线程模型下,对象的状态不在受顺序执行的安全保护,而是需要同步.
    • 同步下可能会出现不一致的问题,如死锁,饥饿.
    • 多线程上下文切换开销可能会导致性能下降.

    线程安全和同步

    线程安全就是正确的同步状态,包括以下几种方式:
    - 无状态(把对象定义成不可变(immutable))
    - 不共享状态(通过线程封闭技术)
    - 在访问状态变量时使用同步机制.(如对象内部锁的synchronized关键字)

    同步机制的目标是实现原子性,避免竞争条件.
    - 原子性是一个操作只会一次性完成, 完成前不会被打断.系统有些操作是原子性,它属于内存模型的一部分,另一些对象也可以实现原子操作,它属于同步机制.
    - 常见的竞争条件包括:读取-修改-写入,先检查后执行.大多数可以分成读写两步或以上的操作都是竞争条件.
    - 最基本的同步机制是加锁,即使用synchronized关键字.把一组可能的竞争条件放到同步机制.读写都需要加锁.注意同一个竞争条件的锁需要一致.

    性能

    加锁同步可能导致性能下降,需要在线程安全的情况下,尽量小的范围加锁可以用小范围的多次加锁取代一次大范围加锁.特别是费时操作不应在有锁的情况下进行.
    ** 使用其它同步机制可以提高性能.

    线程间状态共享

    对象的状态应在对象可控的范围内维护,如果超出了这个范围,即是逸出(Escape).实践上,对象状态应通过方法来访问和修改,如果直接返回对象内部的引用,很可能是逸出.
    

    特别地:
    1. 内部类可以隐含的逸出this对象.
    2. 构造函数中传出this引用可能逸出,
    因为this可能还不完整.

    为了安全的共享状态,可以把对象定义成不变类或事实不变类,也可以用线程封闭技术.

    线程封闭

    线程封闭是不共享状态的一种特例,状态只在一个线程中使用,即状态只保存在局部变量或ThreadLocal变量中.

    不变类

    不可变对象是状态与实例绑定的对象,即实例一但创建它的状态就不再改变.新的状态由一个新的实例来表示.不可变对象要满足以下条件:
    1.创建后不能修改,没有公开的修改方法.
    2.所有成员变量都是final
    3.创建过程中没有传出this指针.

    事实不可变对象由一个可变对象管理时,事实不可变对象的引用本身可能是并不安全.因此必须使用线程安全的访问方式.

    线程安全的方式

    1.静态初始化对象引用.
    2.保存为volatile或AtomicReference
    3.正确构造的对象的final域.
    4.保存在由锁保护的域.

    通过组合线程安全的对象来定义新的线程安全对象

    组合

    组合多个线程安全的对象可能得到一个线程安全的对象,但如果对象的不变式不能满足则需要同步机制来保证.即各组成对象之间不独立,而是一个随另一个变化.

    为线程安全的代码添加新功能,最好的办法是组合,并委托,注意不能逸出原来的对象.

    基础构造块

    1.同步容器,包括Vector/HashTable, Collections.synchronizedXXX.它们都使用内部锁.同步容器在遍历时可能需要加锁,更好的办法是在副本上遍历.
    2.并发容器,包括java.utils.concurrent包内,如concurrentHashMap, CopyOnWriteArrayList, BlockingQueue,ConcurrentListedQueue.它们不需要在遍历过程中加锁.

    同步工具

    1. CountDownLatch可以用于等待多个事件都发生后,才同时启动所有线程.例如等待所有线程都退出.
    2. FutureTask可用于长期计算.
  • 相关阅读:
    DB2原因码7,解决方法
    打包项目成war包并部署到服务器上,项目运行一直显示加载中
    解决js中对象中属性是数组中对应元素,不能使用点数组元素(.数组[i])来获取value值来循环,属性不能是数组元素array[i]的问题
    javaweb项目中jsp的from表单提交action内容与web.xml的servlet-mapping对应
    Collection迭代器Iterator的使用
    使用switch计算出某年某月某日是今年的第几天,输出一直是当月天数
    DB2添加联合主键
    您的主机中的软件中止了一个已建立的连接
    java中在构造方法中修改线程名,修改失败原因(现已修改成功)
    String字符串加号的作用与基本数据类型加号的作用的区别
  • 原文地址:https://www.cnblogs.com/ahuangliang/p/abstract-java-concurrent-in-practice.html
Copyright © 2011-2022 走看看