a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 88|回复: 0

[其他] java并发编程实践过程记录

[复制链接]
发表于 2012-8-4 12:28:23 | 显示全部楼层 |阅读模式
1, 保证线程安全的三种方法: a, 不要跨线程访问共享变量b, 使共享变量是final类型的c, 将共享变量的操作加上同步   2, 一开始就将类设计成线程安全的, 比在后期重新修复它,更容易." j) y5 x% ~# D2 H5 _
  3, 编写多线程程序, 首先保证它是正确的, 其次再考虑性能.$ s- s6 \4 {2 Y3 |3 n/ y
  4, 无状态或只读对象永远是线程安全的.) M. }& z$ c5 ~8 n* n6 ?6 s9 v
  5, 不要将一个共享变量裸露在多线程环境下(无同步或不可变性保护)
/ b; |' |$ j4 u+ A2 X  6, 多线程环境下的延迟加载需要同步的保护, 因为延迟加载会造成对象重复实例化; Q+ k8 @$ o! b5 o3 \
  7, 对于volatile声明的数值类型变量进行运算, 往往是不安全的(volatile只能保证可见性,不能保证原子性).详见volatile原理与技巧中, 脏数据问题讨论.
3 C. h7 o: r" ^! O# s3 J$ O  8, 当一个线程请求获得它自己占有的锁时(同一把锁的嵌套使用), 我们称该锁为可重入锁.在jdk1.5并发包中, 提供了可重入锁的java实现-ReentrantLock.  O8 F0 e" m( O, }- w
  9, 每个共享变量,都应该由一个唯一确定的锁保护.创建与变量相同数目的ReentrantLock, 使他们负责每个变量的线程安全.* E4 k1 k$ h  e; \. F- d9 M. W) G
  10,虽然缩小同步块的范围, 可以提升系统性能.但在保证原子性的情况下, 不可将原子操作分解成多个synchronized块.
0 m! ^* w0 k$ [7 r  11, 在没有同步的情况下, 编译器与处理器运行时的指令执行顺序可能完全出乎意料.原因是, 编译器或处理器为了优化自身执行效率, 而对指令进行了的重排序(reordering).
: f2 ^  k' [0 K- m# @% N  12, 当一个线程在没有同步的情况下读取变量, 它可能会得到一个过期值, 但是至少它可以看到那个线程在当时设定的一个真实数值. 而不是凭空而来的值. 这种安全保证, 称之为最低限的安全性(out-of-thin-air safety)# M  W3 Y4 ]" H! d: H
  在开发并发应用程序时, 有时为了大幅度提高系统的吞吐量与性能, 会采用这种无保障的做法.但是针对, 数值的运算, 仍旧是被否决的.8 x3 p! K  H: u4 S5 d
  13, volatile变量,只能保证可见性, 无法保证原子性.
9 A& t+ c, i6 N5 g+ k8 y9 p  14, 某些耗时较长的网络操作或IO, 确保执行时, 不要占有锁.2 O8 s# u( h& s4 d, l
  15, 发布(publish)对象, 指的是使它能够被当前范围之外的代码所使用.(引用传递)对象逸出(escape), 指的是一个对象在尚未准备好时将它发布.
* h, X$ L5 ?. c  原则: 为防止逸出, 对象必须要被完全构造完后, 才可以被发布(最好的解决方式是采用同步)' o: n: G9 W9 p) p) e& w$ |7 K
  this关键字引用对象逸出
; `- n8 v* U0 p. Q& U  例子: 在构造函数中, 开启线程, 并将自身对象this传入线程, 造成引用传递.而此时, 构造函数尚未执行完, 就会发生对象逸出了.. x) R3 w6 }: `6 D2 O
  16, 必要时, 使用ThreadLocal变量确保线程封闭性(封闭线程往往是比较安全的, 但一定程度上会造成性能损耗)封闭对象的例子在实际使用过程中, 比较常见, 例如 hibernate openSessionInView机制, jdbc的connection机制.8 F  m# @4 G# r$ T' _0 _. Q8 o6 t
  17, 单一不可变对象往往是线程安全的(复杂不可变对象需要保证其内部成员变量也是不可变的)良好的多线程编程习惯是: 将所有的域都声明为final, 除非它们是可变的
7 P' y8 d. v( \' z2 j7 M18, 保证共享变量的发布是安全的a, 通过静态初始化器初始化对象(jls 12.4.2叙述, jvm会保证静态初始化变量是同步的) b, 将对象申明为volatile或使用AtomicReference c, 保证对象是不可变的d, 将引用或可变操作都由锁来保护
8 j9 O9 ^/ I4 C( u  z  19, 设计线程安全的类, 应该包括的基本要素: a, 确定哪些是可变共享变量b, 确定哪些是不可变的变量c, 指定一个管理并发访问对象状态的策略
$ D/ A- t! C, |( o1 Q  20, 将数据封装在对象内部, 并保证对数据的访问是原子的.建议采用volatile javabean模型或者构造同步的getter,setter.
+ s+ N% }+ O# ?7 m$ Y0 t  21, 线程限制性使构造线程安全的类变得更容易, 因为类的状态被限制后, 分析它的线程安全性时, 就不必检查完整的程序.
) M8 l0 c, b- U7 \5 \* K  22, 编写并发程序, 需要更全的注释, 更完整的文档说明.
, z: t( T% Q8 H: z, y  23, 在需要细分锁的分配时, 使用java监视器模式好于使用自身对象的监视器锁.前者的灵活性更好.
- A7 R. h! ^2 U) J  Object target = new Object();/ n, a2 f8 {" e( ]* F# X7 U: n% ~
  // 这里使用外部对象来作为监视器, 而非this' o# m- W6 ^% a) I9 M
  synchronized(target) {
  E. B+ U9 B% ]. |  // TODO
2 \8 L4 F7 s% e8 d# a; h3 J  }8 U) k' G7 A  x
  针对java monitor pattern, 实际上ReentrantLock的实现更易于并发编程.功能上, 也更强大.$ K5 y( ~1 w& P  l8 _
  24, 设计并发程序时, 在保证伸缩性与性能折中的前提下, 优先考虑将共享变量委托给线程安全的类.由它来控制全局的并发访问.& a0 o8 h' b; O) `& R% s* c5 Z
  25, 使用普通同步容器(Vector, Hashtable)的迭代器, 需要外部锁来保证其原子性.原因是, 普通同步容器产生的迭代器是非线程安全的.1 c5 i9 ?' p3 A' ~$ C2 l/ Q
  26, 在并发编程中, 需要容器支持的时候, 优先考虑使用jdk并发容器(ConcurrentHashMap, ConcurrentLinkedQueue, CopyOnWriteArrayList...).
3 u* t; h5 h  A! P  u2 d: ]& j  27, ConcurrentHashMap, CopyOnWriteArrayList并发容器的迭代器,以及全范围的size(), isEmpty() 都表现出弱一致性.他们只能标示容器当时的一个数据状态. 无法完整响应容器之后的变化和修改.
, i3 H6 P8 j* ^' @2 i5 x% i  28, 使用有界队列, 在队列充满或为空时, 阻塞所有的读与写操作. (实现生产-消费的良好方案) BlockQueue下的实现有LinkedBlockingQueue与ArrayBlockingQueue, 前者为链表, 可变操作频繁优先考虑,后者为数组, 读取操作频繁优先考虑. PriorityBlockingQueue是一个按优先级顺序排列的阻塞队列, 它可以对所有置入的元素进行排序(实现Comparator接口)/ E! y' n% {5 x# O
  29, 当一个方法, 能抛出InterruptedException, 则意味着, 这个方法是一个可阻塞的方法, 如果它被中断, 将提前结束阻塞状态.当你调用一个阻塞方法, 也就意味着, 本身也称为了一个阻塞方法, 因为你必须等待阻塞方法返回.
$ p! W" ]5 D; ^; {. a: u& J  如果阻塞方法抛出了中断异常, 我们需要做的是, 将其往上层抛, 除非当前已经是需要捕获异常的层次.如果当前方法, 不能抛出InterruptedException, 可以使用Thread.currentThread.interrupt()方法, 手动进行中断.
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Woexam.Com ( 湘ICP备18023104号 )

GMT+8, 2024-5-5 21:36 , Processed in 0.355512 second(s), 21 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表