【Java-基础系列】Synchronized关键字

初识

synchronize

翻译——同步(cause to occur or operate at the same time or rate.)

一般用于多线程操作,保证操作的同步性

消除线程切换时导致的共享内存的内容不可预测性

典型应用

单例模式的double-check

MacGesture MacGesture, Today at 12.45.47 AM

题外话:单例模式很容易引发内存泄露,在项目中要少用;

另外单例模式的实现形式中,使用enum枚举是比较简洁的形式

常见使用形式

  • 锁方法

    MacGesture MacGesture, Today at 12.16.25 AM

  • 锁代码段

    • 用this关键字 sychronized(this)

    MacGesture MacGesture, Today at 12.21.38 AM

    • 用锁对象 sychronized(lockObjct)

    MacGesture MacGesture, Today at 12.17.56 AM

官方教程

在Oracle的Java 文档里头有对Synchronization的详细解释,分为如下五部分展开:

  1. 线程接口(Thread Interference)

    【Java-揭面系列】Synchronized关键字.md Typora, Today at 12.14.14 AM

    不同线程对同一份数据的操作如果是穿插进行的,会导致不可预料的结果,这主要是因为CPU会让多个线程分片执行,各个线程对此数据的操作也是互不可知的,这样就很容易引发内存不按照预期改变的情况

  2. 内存连续性错误(Memory Consistency Errors)

    例子如上图,若三步操作不仅由一个线程执行,就很有可能导致打印出的并不是1而是0

  3. 同步方法(Synchronized Methods)

    • Java中在方法体上添加synchronized关键字,能够保证同一时间仅有一个线程在调用某个对象的该方法;
    • 同时Java还会为该方法在与对象之间建立一个happens-before关系,保证对象的该状态对所有线程都是可见的
  1. 固有锁与同步(Intrinsic Locks and Synchronization)

    MacGesture MacGesture, Today at 12.16.25 AM

    当一个线程调用synchronized方法时,它会自动获取该方法对象的固有锁,并在方法返回时释放它。即使返回是由未捕获的异常引起的,也会释放锁。

    MacGesture MacGesture, Today at 12.16.56 AM

    直接使用this当做锁的关键字时,改对象的所有使用this当关键字的操作都必须保证同时只有一个线程访问,这样做虽然简便,但时间上有效率浪费,因为该对象的所有操作并不一定要保持完全同步,👇的代码段就解决了这一问题:

    通过细粒度的锁来提高并发性:

    MacGesture MacGesture, Today at 12.22.45 AM

    MsLunch有两个实例字段,c1并且c2从不一起使用。这些字段的所有更新必须同步,但是没有理由阻止c1的更新与c2的更新进行交错 - 这样做会通过创建不必要的阻止来降低并发性。而不是使用同步方法或以其他方式使用与之关联的锁this

  2. 原子操作(Atomic Access )

    原子操作保证了操作的完整性:要么不执行,执行的话就一定要完全执行(即使虚拟机GC也不能打断)

    MacGesture MacGesture, Today at 12.35.40 AM

    第一条指的是我们可以使用Java内附的AtomicIntegerAtomicBoolean等变量来直接利用这一特性

    第二条引出了另一个关键字volatile,这实际上需要另起一篇进行介绍了

总结

synchronize在解决多线程编程问题时有奇效,但有一定的效率牺牲

使用时应尽量减小锁定的范围

对并发性有要求的地方需要注意看是否有细化锁粒度的必要

推荐阅读

Powered by KyleCe

Copyright © 2015 - 2019 KyleCe All Rights Reserved.

访客数 : | 访问量 :