您的位置:首页 > 其它

线程同步问题,volatile关键字和synchronized关键字

2016-01-14 14:47 274 查看
本文主要资源来自Effective Java这本书,相当于读书笔记一样,所属权属于该书作者。

1.同步访问共享的可变数据
关键字synchronized可以保证在同一个时刻。只有一个线程可以执行某一个方法,或者某个代码块。

java语言规范保证读或写一个变量是原子的,除非这个变量的类型是long或者double。
为了在线程之间进行可靠的通信,也为了互斥访问,同步是必要的。


(本图片主要来自书籍截图。)

while(!done){
   i++;
}
会变成:
if(!done){
   while(true){
          i++;
}
}
解决方法:



2.volatile关键字,注意:这个关键字在使用n++,n=n+1等,是没有用的,因为不是原子性。

虽然volatile修饰符不执行互斥访问,但它保证任何一个线程在读取该域的时候将看到最近刚刚写入的值。

public class Stop{
private static volatile boolean stoopRequested;
public static void main(String[] args){
Thread backgroundThread = new Thread(new Runnable(){
public void run(){
int i =0;
while(!stoopRequested)
i++;
}
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
stoopRequested =true;
}
}使用的时候要小心处理。

问题在于,增量操作符(++)不是原子的。它在nextSerialNumber域中执行两项操作:首先它读取值,然后写一个新值

,然后写回一个新值,相当于原来的值再加上1.如果第二个线程在第一个线程读取旧值和写回新值间读取这个域,

第二个线程就会与第一个线程一起看到同一个值,并返回相同的序列号。这就是安全性失败。

修正

一种方法是在它的声明中增加synchronized修饰符。一旦这么做,就可以且应该从nextSerialNumber中删除volatile修饰符。为了这个方法更可靠要有long代替int,或者在nextSerialNumber快要重叠时抛出异常。

通常,你应该再同步区域内做尽可能少的工作。



性能问题:

StringBuffer基本被StringBuilder代替,因为StringBuffer实例几乎总是被用于单个线程中,而他们执行的却是内部同步。

当你不确定的时候,就不要同步你的类,而是应该建立文档,注明它不是线程安全的。

当你在设计一个可变类的时候,要考虑一下它们是否应该自己完成同步操作。

4.线程安全性的文档化
这写文档的时候,表明那个是同步线程的类,或者方法。方便处理。
5.慎用延迟初始化
就像大多数的优化一样,对于延迟初始化,最好建议“除非绝对必要,否则就不要这么做”。
它降低了初始化类或者创建实例的开销,却增加了访问被延迟初始化的域的开销。



如果处于性能的考虑而需要对实例域使用延迟加载初始化,就使用双重检查模式。



但是性能问题大大折扣,进行改进(注意是在jdk1.6之后,前面的jdk1.5):

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: