Java中synchronized 关键字和volatile关键字

synchronized 语法使用

  • 1、静态方法
  • 2、实力方法
  • 3、代码块synchronized(对象){
    //TODO
    }
  • 1、介绍

    synchronized关键字是Java提供的一种原子性内置锁,线程的执行代码在进入synchronized代码块前会自动获取内部锁,这时候其他线程访问该同步代码块的时会被阻塞挂起。

    也就是说,各个线程竞争同一把锁,如果竞争成功,就继续往下执行,否则就被挂起(阻塞在当前行),当此线程正常退出或者抛出异常后(释放对象锁),通知JVM及系统,其他的线程可以来竞争这把锁
    在这里插入图片描述
    而Java中的线程和操作系统中的原生线程是一一对应的,因此当阻塞一个线程时候,需要从用户态切换到内核态来进行阻塞,我们知道切换这个过程是一个耗时 的操作,而synchronized的使用就会导致上下文切换

  • 2、加锁操作

  • 对哪一个对象进行加锁操作(一个对象只有一把锁)
  • 只有同一个对象,才会存在同步互斥的作用(线程安全的三大特性都能满足)
  • 对于synchronized内部的代码来说,同一个时间点只有一个线程在执行(不存在并发、并行)
  • 运行的线程数量越多,性能下降的越快(归还对象锁的时候,就会有很多的线程不停在被唤醒,阻塞状态间进行切换)
    在这里插入图片描述
  • 3、内存语义

    • 1、内存语义也就是说可以解决共享变量内存可见性问题。
  • 进入synchronized 块内的语义是:在synchronized 块内使用的变量从线程的工作内存进行清除,这样在synchronized
    块内使用的该变量就不会从该线程工作内存获取,而是直接从主内存获取。
  • 退出代码块的与语义是:将synchronized 块内对共享变量修改刷新到主内存

在这里插入图片描述

  • 2、原子性问题
    原子性操作,就是执行一系列操作的时候要么全部执行,要么全部不执行,如果在执行中别的线程代码也在执行,那结果结汇产生问题。使用synchronized 可以避免这个问题,同步代码块,一个线程在执行操作的时候,其他的线程不能插入。而是在同步代码块那一行进行阻塞等待。
    在这里插入图片描述

volatile关键字

该关键字可以保证对一个变量的更新对其他线程立马可见(解决可见性问题),还有指令重排序问题。

  • 当一个变量被volatile修饰时,线程在写入变量时候,不会把值缓存在寄存器或者其他地方,而是会把值刷新到主内存。当其他线程读取该变量时,会从主内存获取

    注意synchronized和volatile都可以解决可见性问题

    两者都可以解决可见性问题,但是使用锁相对于比较笨重,耗费时间,因为它会带来线程上下文切换的时间开销

  • 重排序问题:
    Java内存模型允许编译器和处理器对指令进行重排序以此来提高运行的性能,但是是有一个前提是,仅仅是会对不存在数据依赖性的指令进行重排序。
    在单线程下,不论是程序顺序执行还是重排序执行,最终的结果都是一样的,但是在多线程下就会出现问题。
    在这里插入图片描述

  • 使用volatile的情况

1、当写入变量值不依赖变量的当前值。因为如果依赖当前值,将是获取- 计算 - 写入三步操作,这三步不是原子性的,而volatile不支持原子性。
2、读写变量时没有加锁。因为加锁本身已经保证了可见性问题,就不用volatile修饰

在这里插入图片描述
总结:

volatile关键字可以避免指令重排序问题和可见性问题
synchronized 关键字可以解决可见性问题,原子性问题

猜你喜欢

转载自blog.csdn.net/qq_44723296/article/details/105412662