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

本文主要资源来自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):
















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值