并发(三):理解安全的发布

我们常说,如果将对象放入到线程安全容器,例如Vector或synchronizedMap时,那么对象就可以安全地发布,后续的每个线程都可以安全地访问。

按照我们上一节的论述,这里的安全包含对象的内蕴状态吗,还是依旧仅仅只有对象的引用?

经过测试,我们可以发现,所有的线程安全容器依旧只能保证对象的引用更改是安全的,也就是说,只有当对象的引用地址发生了变化,其他线程才能感知到这种变化。

示例代码如下,依旧以计数为例:

//  类本身不是线程安全的,绝不会因为放入线程安全容器,就会变成线程安全的类了
static class Person {

    private Long age = 0L;

    // 总是递增
    public  void increment() {
        //  如果是可见的,age的读取与写入应该可以保持一致
        age ++;
    }

    public  long getAge() {
        return age;
    }

}

测试代码如下:

final String KEY = "KEY";
final Person person = new Person();
//  放入线程安全容器
final Map<String, Person> values = Collections.synchronizedMap(Maps.newHashMap(KEY, person));
//  四个线程同时计数
for (int i = 0; i < 4; i++) {
    new Thread() {
        public void run() {
            int count = 25;
            while (count > 0) {
                Person yiifaa = values.get(KEY);
                yiifaa.increment();
                count--;
            }
        }

    }.start();
}

从结果来看,最后“person.getAge()”依旧不能达到100的计数,所以对象的内容依旧不是线程安全的。

下面说简单的解决办法,因为对象的获取是线程安全的,但递增的方法(yiifaa.increment())是不安全的,所以只需要最小的锁同步,如下:

Person yiifaa = values.get(KEY);
//  当然观察锁也不一定要是yiifaa,只要是共享对象都可以,如KEY
synchronized(yiifaa) {
    yiifaa.increment();                 
}
count--;    

从上面的代码可以看出,锁与共享变量并没有直接的关系,只需要保证所有的线程在同一个锁上同步即可。

结论

在使用synchronizedMap、synchronizedList、ConcurrentMap时要千万注意,线程安全容器仅仅只能保证对象的引用可见性,而不能内容的可见性。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值