二、同步不是敌人

    在Java程序中,用来管理线程间协调的主要工具是syschronized关键字。当缺少同步的情况下,jvm可以很轻松的对不同线程的执行操作进行计时和排序,这可以在大部分情况下提高性能,但是也给程序员带来了额外的负担,必须识别可能危机的程序正确性。

Syschronized真正意味着什么?

     刚开始以为同步代码块、同步方法理解为使用互斥,原子操作。但是还有更复杂的应该清除。
     syschronized语义确实保证了一次只有一个线程可以访问被保护的区段。*****但同时还包括同步线程在主存内相互作用的规则。*****在缺少同步的情况下,JVM允许两个线程在同一个内存地址看到不一样的值,而当用锁进入同步时,JVM立马要求线程工作缓存中的值失效,并在所释放前对她进行刷新并将修改过的值写会主存。不难看出为什么同步对程序影响这么大,频繁的刷新缓存代价很大。

选择一条好的运行线路

   如果同步使用不恰当,对共享资源会造成数据混乱和争用情况,导致程序崩溃或者不可知的运行,更糟的是有些情况是偶然发生的,而我们得出程序正确的结论。另一方面,同步使用不当会造成性能降低或死锁,因此编写优秀的多线程程序需要使用好的运行线路,足够的同步使数据不发生紊乱,但不滥用到承担死锁或不必要的削减程序性能的风险。

同步的代价,不要争用

 程序即使只包括一个在单处理器上运行的单线程,一个同步方法的调用比非同步调用也慢很多,如果同时发生锁争用,性能会降低更多。但是随着JVM的优化,对同步的代价不断降低,反而是争用情况会造成极大性能损失。那么减少争用需要考虑减小锁的粒度,减小同步块大小以及减小线程间共享资源的数量。

什么时候需要同步

简单说,存在共享数据时需要考虑,一般情况可使用volatile保护数据字段,其他情况,可在读写共享数据前请求加锁,一个好的建议是明确指出用什么锁来保护给定字段或对象,并在代码中记录。
另外,简单的同步存取器方法(或者声明下层字段为volatile)并不能保护一个共享字段。例:

private int foo;
public synchronized int getFoo() { return foo; }
public synchronized void setFoo(int f) { foo = f; }`
如果一个调用者想增加foo属性值,下面代码不是线程安全的:
setFoo(getFoo() + 1);
如果两个线程同时增加foo值,结果可能增加1或2,调用者需要同步一个锁,或者进行同步复合操作,才能防止这种争用情况。一个好的方法是在JavaDoc类中指定同步哪个锁,这样调用者就不用猜了。

如果情况不确定,考虑同步包装

当写一个类的时候,不确定时候用在共享环境,或者不清楚适合的锁粒度多大,可以通过同步包装来解决。例如Collections类,它是非同步的,但在框架定义中每个接口都有一个同步包装(例:Collctions.synchronizedMap(),它用一个同步的版本包装每个方法)。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值