java并发编程(三)对象共享

可见性

很多人认为synchronized只能用于实现原子性或者确定临界区,忽视了它另一个重要的作用,确保内存可见性

每个线程都有自己的工作内存。线程对变量的所有操作都必须在工作内存中进行,而不能直接对主存进行操作。并且每个线程不能访问其他线程的工作内存。所以在写线程没有将工作内存的数据同步到主存前其他线程是无法看先写入的值的。

指令重排序就是在不影响程序逻辑结果时为了充分利用cpu指令的执行顺序可能不是按照代码顺序执行。
例如实例化一个对象其实可以分为下面的步骤:
1.申请一块内存空间;
2.在这块空间里实例化对象;
3.instance的引用指向这块空间地址;
在JVM的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。

非原子的64位操作
线程在没有同步的时候可能会读取到一个无效值,但是这个值至少是之前线程写入的并不是随机的,这种安全性保证叫最低安全性。

最低安全性适用于大多数变量,但是存在例外,未同步的64位数值变量(double,long),对于这两种类型的变量,jvm允许64位的读写操作分解成两个32位操作,这可能会造成读线程可能会读到更新过的高32位数据和未更新的低32位数据。这完全是一个错误的数据。所以针对这两种变量一定要做数据同步操作。

两种保证可见性的操作
加锁 加锁可以保证所有线程都能看到共享变量的最新值
volatile volatile是一种稍弱的同步机制,确保线程的每次写入都会直接同步到主存中,并禁止指令重排。在synchronied优化后很少使用volatile关键字了,更多可能只是避免指令重排
加锁既可以保证原子性又可以保证可见性,volatile只能保证可见性。

对象的发布与逸出

简单理解发布其实就是声明非私有的全局变量,而逸出是发布了不该发布的对象。

有一种特殊的发布是在构造函数中注册一个监听器或者线程,都会使监听器或线程内的对象被发布。如果一定要这么做,可以将构造函数定义私有,新建工厂方法来创建对象。

为什么在日常开发中大部分的全局变量都会声明为私有的,因为当这些变量不是私有那么就给了其他调用线程修改变量的可能。这样这个对象就是一个线程不安全的对象了。
线程封闭
之前的博客提到线程是无处不在的,但是为什么我们日常开发中却很少做线程安全方面的控制呢?
其实保证线程安全并不意味着必须要加锁,线程封闭就是一个方法。
线程封闭确保了没有其他线程能够修改对象的属性,这也是实现线程安全最简单的方式之一。
保证线程封闭有三种方式:
ad-hoc线程封闭
简单来说就是通过程序代码来保证对象同一时间只被一个线程使用,这是非常脆弱且不安全的做法。
栈封闭
我们局部变量是封闭在执行线程的栈中,其他线程无法访问。这样也就实现了栈封闭,也是我们日常开发经常使用的做法。
ThreadLocal类
ThreadLocal是JDK为我们提供的实现线程封闭的方法。
ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。也就是说这个变量是每个线程之间独立的,线程改变其值只影响该线程内的数据。从而实现线程封闭。
注意ThreadLocal可能会发生内存泄露。当某个ThreadLocal变量不再使用时,记得调用remove()方法。

不可变对象

如果某个对象创建之后其状态就不可改变,那么这个对象就是不可变对象。
不可变对象也是保证线程安全的一种方式。
满足不可变对象的三个条件:

  • 对象创建以后状态不能修改
  • 对象的所有全局变量都是final类型的
  • 对象是正确创建的(构造函数内没有上文提到的逸出)

安全的共享对象

当某个对象必须要被多线程共享时有四种方案实现线程安全。

  • 线程封闭 线程封闭的对象只能由一个线程拥有,对象被封闭在该线程中。
  • 只读共享 在没有额外同步的情况下可以并发访问但是没有线程可以修改。不可变对象
  • 对象安全共享 如果一个对象要被共享,在开发时在类的内部实现同步。
  • 保护对象 与对象安全共享相反,在发布对象时对对象进行加锁。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值