初识
synchronize
翻译——同步(cause to occur or operate at the same time or rate.)
一般用于多线程操作,保证操作的同步性
消除线程切换时导致的共享内存的内容不可预测性
典型应用
单例模式的double-check
题外话:单例模式很容易引发内存泄露,在项目中要少用;
另外单例模式的实现形式中,使用
enum
枚举是比较简洁的形式
常见使用形式
锁方法
锁代码段
- 用this关键字
sychronized(this)
- 用锁对象
sychronized(lockObjct)
- 用this关键字
官方教程
在Oracle的Java 文档里头有对Synchronization的详细解释,分为如下五部分展开:
线程接口(Thread Interference)
不同线程对同一份数据的操作如果是穿插进行的,会导致不可预料的结果,这主要是因为CPU会让多个线程分片执行,各个线程对此数据的操作也是互不可知的,这样就很容易引发内存不按照预期改变的情况
内存连续性错误(Memory Consistency Errors)
例子如上图,若三步操作不仅由一个线程执行,就很有可能导致打印出的并不是1而是0
同步方法(Synchronized Methods)
- Java中在方法体上添加
synchronized
关键字,能够保证同一时间仅有一个线程在调用某个对象的该方法;
- 同时Java还会为该方法在与对象之间建立一个happens-before关系,保证对象的该状态对所有线程都是可见的
- Java中在方法体上添加
固有锁与同步(Intrinsic Locks and Synchronization)
当一个线程调用
synchronized
方法时,它会自动获取该方法对象的固有锁,并在方法返回时释放它。即使返回是由未捕获的异常引起的,也会释放锁。直接使用this当做锁的关键字时,改对象的所有使用this当关键字的操作都必须保证同时只有一个线程访问,这样做虽然简便,但时间上有效率浪费,因为该对象的所有操作并不一定要保持完全同步,👇的代码段就解决了这一问题:
通过细粒度的锁来提高并发性:
类
MsLunch
有两个实例字段,c1
并且c2
从不一起使用。这些字段的所有更新必须同步,但是没有理由阻止c1的更新与c2的更新进行交错 - 这样做会通过创建不必要的阻止来降低并发性。而不是使用同步方法或以其他方式使用与之关联的锁this
原子操作(
Atomic
Access )原子操作保证了操作的完整性:要么不执行,执行的话就一定要完全执行(即使虚拟机GC也不能打断)
第一条指的是我们可以使用
Java
内附的AtomicInteger
、AtomicBoolean
等变量来直接利用这一特性第二条引出了另一个关键字
volatile
,这实际上需要另起一篇进行介绍了
总结
synchronize在解决多线程编程问题时有奇效,但有一定的效率牺牲
使用时应尽量减小锁定的范围
对并发性有要求的地方需要注意看是否有细化锁粒度的必要