posts - 495,  comments - 11,  trackbacks - 0

1,保证线程安全的三种方法:
    a,不要跨线程访问共享变量

    b,使共享变量是
final类型的
    c,将共享变量的操作加上同步


2,一开始就将类设计成线程安全的
,比在后期重新修复它,更容易.

3,编写多线程程序
,首先保证它是正确的,其次再考虑性能
.

4,无状态或只读对象永远是线程安全的
.

5,不要将一个共享变量裸露在多线程环境下
(无同步或不可变性保护
)

6,多线程环境下的延迟加载需要同步的保护
,因为延迟加载会造成对象重复实例化


7,对于
volatile 声明的数值类型变量进行运算,往往是不安全的(volatile 只能保证可见性, 不能保证原子性).
详见
volatile 原理与技巧中,脏数据问题讨论
.

8,当一个线程请求获得它自己占有的锁时
( 同一把锁的嵌套使用),我们称该锁为可重入锁.
jdk1.5 并发包中,提供了可重入锁的java 实现
-ReentrantLock.

9,每个共享变量
, 都应该由一个唯一确定的锁保护.
创建与变量相同数目的
ReentrantLock,使他们负责每个变量的线程安全
.

10,虽然缩小同步块的范围
,可以提升系统性能.
但在保证原子性的情况下
,不可将原子操作分解成多个synchronized
.

11,在没有同步的情况下
,编译器与处理器运行时的指令执行顺序可能完全出乎意料
.
原因是
,编译器或处理器为了优化自身执行效率,而对指令进行了的重排序
(reordering).

12,当一个线程在没有同步的情况下读取变量
,它可能会得到一个过期值,但是至少它可以看到那个

线程在当时设定的一个真实数值
.而不是凭空而来的值.这种安全保证,称之为最低限的安全性(out-of-thin-air safety)

在开发并发应用程序时
,有时为了大幅度提高系统的吞吐量与性能,会采用这种无保障的做法
.
但是针对
,数值的运算,仍旧是被否决的
.

13,volatile 变量
, 只能保证可见性,无法保证原子性
.

14,某些耗时较长的网络操作或IO,确保执行时,不要占有锁.

15,发布(publish) 对象,指的是使它能够被当前范围之外的代码所使用.( 引用传递
)
对象逸出(escape),指的是一个对象在尚未准备好时将它发布.

原则:为防止逸出,对象必须要被完全构造完后,才可以被发布( 最好的解决方式是采用同步
)

this
关键字引用对象逸出

例子:在构造函数中,开启线程,并将自身对象this 传入线程,造成引用传递.
而此时,构造函数尚未执行完,就会发生对象逸出了
.

16,必要时,使用ThreadLocal变量确保线程封闭性(封闭线程往往是比较安全的,但一定程度上会造成性能损耗)
封闭对象的例子在实际使用过程中,比较常见,例如hibernate openSessionInView机制, jdbcconnection机制.

17,单一不可变对象往往是线程安全的(复杂不可变对象需要保证其内部成员变量也是不可变的)
良好的多线程编程习惯是:将所有的域都声明为final,除非它们是可变的

18,保证共享变量的发布是安全的

    a,
通过静态初始化器初始化对象(jls 12.4.2 叙述, jvm 会保证静态初始化变量是同步的)
    b,
将对象申明为volatile 或使用
AtomicReference
    c,
保证对象是不可变的

    d,
将引用或可变操作都由锁来保护

19,设计线程安全的类,应该包括的基本要素:
    a,确定哪些是可变共享变量
    b,确定哪些是不可变的变量
    c,指定一个管理并发访问对象状态的策略

20,将数据封装在对象内部,并保证对数据的访问是原子的
.
建议采用volatile javabean 模型或者构造同步的getter,setter.

21,线程限制性使构造线程安全的类变得更容易,因为类的状态被限制后,分析它的线程安全性时,就不必检查完整的程序.

22,
编写并发程序,需要更全的注释,更完整的文档说明.

23,在需要细分锁的分配时,使用java监视器模式好于使用自身对象的监视器锁.
前者的灵活性更好.

Object target = new Object();
//这里使用外部对象来作为监视器,而非this
synchronized(target) {
    // TODO
}

针对java monitor pattern,实际上ReentrantLock的实现更易于并发编程.
功能上,也更强大.

24,设计并发程序时,在保证伸缩性与性能折中的前提下,优先考虑将共享变量委托给线程安全的类
.
由它来控制全局的并发访问
.

25,使用普通同步容器(Vector, Hashtable) 的迭代器,需要外部锁来保证其原子性
.
原因是,普通同步容器产生的迭代器是非线程安全的
.

26,在并发编程中,需要容器支持的时候,优先考虑使用jdk 并发容器

(ConcurrentHashMap, ConcurrentLinkedQueue, CopyOnWriteArrayList...).


27, ConcurrentHashMap, CopyOnWriteArrayList

并发容器的迭代器, 以及全范围的size(), isEmpty()都表现出弱一致性
.
他们只能标示容器当时的一个数据状态.无法完整响应容器之后的变化和修改
.

28,
使用有界队列,在队列充满或为空时,阻塞所有的读与写操作. ( 实现生产- 消费的良好方案)

BlockQueue
下的实现有LinkedBlockingQueue ArrayBlockingQueue,前者为链表,可变操作频繁优先考虑, 后者为数组,读取操作频繁优先考虑.
PriorityBlockingQueue
是一个按优先级顺序排列的阻塞队列,它可以对所有置入的元素进行排序( 实现Comparator 接口
)

29,
当一个方法,能抛出InterruptedException,则意味着,这个方法是一个可阻塞的方法,如果它被中断,将提前结束阻塞状态
.
当你调用一个阻塞方法,也就意味着,本身也称为了一个阻塞方法,因为你必须等待阻塞方法返回
.

如果阻塞方法抛出了中断异常,我们需要做的是,将其往上层抛,除非当前已经是需要捕获异常的层次
.
如果当前方法,不能抛出InterruptedException,可以使用Thread.currentThread.interrupt() 方法,手动进行中断.

 

posted on 2011-05-18 22:53 jadmin 阅读(159) 评论(0)  编辑  收藏

只有注册用户登录后才能发表评论。


网站导航: