Java锁机制--synchronized

来源:互联网 发布:知乎搞笑的事情 编辑:程序博客网 时间:2024/05/16 18:27

在理解锁机制前,我们需要了解线程安全和并发相关的知识。

Java内存模型

  • lock(锁定):作用于主内存的变量,它把一个变量标识为一个线程独占的状态;

  • unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定;

  • read(读取):作用于主内存的变量,它把一个变量的值从主内存传送到线程中的工作内存,以便随后的load动作使用;

  • load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中;

  • use(使用):作用于工作内存的变量,它把工作内存中一个变量的值传递给执行引擎;

  • assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存中的变量;

  • store(存储):作用于工作内存的变量,它把工作内存中的一个变量的值传送到主内存中,以便随后的write操作;

  • write(写入):作用于主内存的变量,它把store操作从工作内存中得到的变量的值写入主内存的变量中。

并发的三个特性

  • 原子性

    原子性是指不可再分的最小操作指令,即单条机器指令,原子性操作任意时刻只能有一个线程,因此是线程安全的。

    Java内存模型中通过read、load、assign、use、store和write这6个操作保证变量的原子性操作。

    long和double这两个64位长度的数据类型java虚拟机并没有强制规定他们的read、load、store和write操作的原子性,即所谓的非原子性协定,但是目前的各种商业java虚拟机都把long和double数据类型的4中非原子性协定操作实现为原子性。所以java中基本数据类型的访问读写是原子性操作。

    对于大范围的原子性保证需要通过lock和unlock操作以及synchronized同步块来保证。

  • 可见性

    可见性是指当一个线程修改了共享变量的值,其他线程可以立即得知这个修改。

    Java内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值来实现可见性的。

    Java中通过volatile、final和synchronized这三个关键字保证可见性:

    • volatile:通过刷新变量值确保可见性。

    • synchronized:同步块通过变量lock锁定前必须清空工作内存中变量值,重新从主内存中读取变量值,unlock解锁前必须把变量值同步回主内存来确保可见性。

    • final:被final修饰的字段在构造器中一旦被初始化完成,并且构造器没有把this引用传递进去,那么在其他线程中就能看见final字段的值,无需同步就可以被其他线程正确访问。

  • 有序性

    线程的有序性是指:在线程内部,所有的操作都是有序执行的,而在线程之间,因为工作内存和主内存同步的延迟,操作是乱序执行的。

    Java通过volatile和synchronized关键字确保线程之间操作的有序性。

    volatile禁止指令重排序优化实现有序性。

    synchronized通过一个变量在同一时刻只允许一个线程对其进行lock锁定操作来确保有序性。

Java锁机制

目前在Java中存在两种锁机制:synchronized和Lock

数据同步需要依赖锁,那锁的同步又依赖谁?synchronized给出的答案是在软件层面依赖JVM,而Lock给出的方案是在硬件层面依赖特殊的CPU指令

synchronized

synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:

  1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;

  2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;

  3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;

  4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

Synchronized的作用主要有三个:

  • 确保线程互斥的访问同步代码

  • 保证共享变量的修改能够及时可见

  • 有效解决重排序问题。

一般说的synchronized用来做多线程同步功能,其实synchronized只是提供多线程互斥,而对象的wait()和notify()方法才提供线程的同步功能。

一般说synchronized是加锁,或者说是加对象锁,其实对象锁只是synchronized在实现锁机制中的一种锁(重量锁,用这种方式互斥线程开销大所以叫重量锁,或者叫对象monitor),而synchronized的锁机制会根据线程竞争情况在运行会有偏向锁、轻量锁、对象锁,自旋锁(或自适应自旋锁)等,总之,synchronized可以认为是一个几种锁过程的封装。

通常说的synchronized在方法或块上加锁,这里的锁就是对象锁(当然也可以在类上面),或者叫重量锁,在JVM中又叫对象监视器(Monitor),就是对象来监视线程的互斥。

总结

synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性。