Java编程保证线程安全的三种方法
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机制,jdbc的connection机制。
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()方法,手动进行中断。
- [游客] 原来如此,以前做的时候也是一跳而过 01/10 23:37
- [游客] 赞 03/19 16:36
- [xzg6145] 我到了课程设计1,后面内容比较容易,等学完再复习下,然后写几个小游戏得瑟下,感觉以后很美好!!! 03/26 10:55
- [xiaoyao2012] 人情情人,人随情,情走人止 11/02 15:53
- [fpamc] 回看这篇文章。。 感受颇有不同。。。 我会继续往下走。。。 06/16 10:14
- [dxiaolin] 受教了 05/31 21:21
- [tomato] 支持你弄个chm出来。 05/29 16:33
- [fpamc] 收藏一下……有时间无聊了就弄chm`(*∩_∩*)′ 05/29 14:16
- [tomato] 这些命令刚动手实验了一遍,收获还是蛮大的。 05/29 13:04
- [chinatree] 在c中取段地址: 例: #include <stdio.h> f1() { re 05/27 10:00
- [游客] [url=http://www.banjia126.com/banjiachangshi/92.ht 07/26 23:46
- [jeremy] 能加你扣扣吗?我的469614823 08/07 14:36
- [e56004474] 好久不见tomato 活跃拉。。 06/18 13:39
- [fpamc] 正在研究17章…… 近期路线是…… 17章实验--复习-滚屏算法-截取中断来应用动态时间-复习- 05/29 14:19
- [xuehuihui] 请问检测点2.2的填空地(2)题第一个空到底是10001H对 还是1001H 对?谢谢 急 05/15 16:34
- [gnunix] 我的检测点3.2第二小题的sp设置的有问题吧? 05/13 09:48
- [gnunix] 谢谢番茄老师的提醒,我会改进的! 05/12 12:20
- [fpamc] 好久没见老师了。每当提交完作业,都希望老师快速的批阅。得到老师小小的夸奖。看见同学们都在努力学习,相 05/05 23:16
- [fpamc] 会的,我会的,我一定会做出来的,而且做到我内心所想的,更重要的,不会让tomato老师失望。 我想 04/21 08:42
- [sbxinran] 谢谢tomato老师给我检查作业,如果我的分析方法有错误还请您多指点指点 04/11 12:43