zoukankan      html  css  js  c++  java
  • Java 线程 — synchronized、volatile、锁

    线程同步基础

    synchronized 和volatile是Java线程同步的基础。

    synchronized

    将临界区的内容上锁,同一时刻只有一个进程能访问该临界区代码
    使用的是内置锁,锁一个时刻只能被一个线程持有,可以重入(表示一个处于synchronized代码中的线程可以进入另外一个使用synchronized的代码快,比如:方法A和方法B同时使用synchronized修饰,在方法A中调用了方法B,调用某个线程调用方法A的时候不会造成死锁,因为synchronized是可重入的锁,线程在进入方法A的时候获得了当前对象的锁,但是此时这个线程依然可以获得方法B的synchronized锁)

    synchronized修饰不同对象时获得的锁:

    • 修饰普通方法:获得的锁是当前对象
    • 修饰静态方法:获得的锁是当前类class
    • 修饰代码块:取决于具体的锁对象

    在Java的同步方法中synchronized是比较重量级的锁,而且不够灵活,jvm提供了更轻量的volatile,jdk提供了更灵活的Lock。

    volatile

    在多处理器的CPU架构下,因为每个处理器都有自己的缓存,线程访问变量的时候会读取缓存,多个线程读取的缓存不一样会导致每个线程得到的值不一样。使用该关键字的效果是:

    • 处理器将缓存写回到内存
    • 处理器将缓存写回到内存的时候会导致其他处理器的内存失效

    作用

    • 保证可见性,Java内存模型(JMM)确保所有线程看到的这个变量的值是一致的
    • 只保证简单操作的原子性(保证变量简单赋值操作的原子性,如:temp = 1,不保证复杂操作的原子性,如:temp++)

    内存语义(就是内存会做的操作)

    • 写:当写一个volatile的变量时,JMM把该线程对应的本地缓存中的共享变量刷新到主内存
    • 读:当读一个volatile变量时,JMM把该线程对应的本地缓存置为无效,线程接下来将从主存中读取共享变量

    问题:上面提到的刷新操作是对这个本地内存刷新,还是只刷新volatile变量?
    解答:是刷新整个本地缓存,包括其他共享变量

    CAS

    CAS:Compare And Switch,在Java中是通过调用C/C++写的本地方法完成的,C又调用了CPU的cmpxchg指令完成的。
    一般来说有三个值:内存值V,期望值A,更新值B,如果内存值和期望值相等,则用更新值B替换内存值A,否则什么也不做

    什么叫锁:锁其实就是维护一种状态,比如一个int状态值state,state变量对于所有线程可见,线程A将state改为1的时候(假设,当然根据具体的需要可以设为对应的值)表示上锁的状态,线程在试图修改state的时候,发现是1,说明其他线程已经改过,线程B则进入阻塞状态,当线程A释放锁的时候,也就是将state改为0的时候,唤醒线程B,线程B会重新试图获取锁,也就是修改state的值,如果state为0,那么修改成功,线程B获得锁

    • 偏向锁:为了让之前获得过该锁的线程更容易获得锁(获得锁的代价更低,不需要CAS加锁和解锁),因为Hotspot的作者研究发现:大多数情况下锁不仅不存在竞争,而且总是由同一线程多次获得

    内存语义

    • 获取锁:当线程获取锁时,JMM会把线程对应的本地缓存中的共享变量刷新到主存
    • 释放锁:当线程释放锁时,JMM会把线程对应的本地缓存置为无效

    对比volatile的和锁的内存语义,volatile的写——锁的获取,volatile的读——锁的释放

    释放锁的线程在释放锁之前可见的变量,在获取锁的线程获取锁之后也可以看见这些变量,volatile也一样

    锁的实现

    1. 利用volatile的读-写内存语义
    2. 利用CAS(CompareAndSet)附带的volatile读-写内存语义(因为在实现CAS的时候汇编会有一个lock前缀,这个前缀会带来和volatile相同的内存语义)

    一些零碎的知识点:

    • JVM在类初始化阶段(Class加载后,且被线程使用前),JVM会获取一个锁,这个锁可以同步多个线程对同一个类的初始化

    锁参考
    CAS参考

  • 相关阅读:
    Android虚拟、实体键盘不能同时使用?
    libwebsockets 运行问题
    Qt TabWidget QTabBar 宽高设置
    I.MX6 recovery mode hacking
    libwebsockets libwebsockets-webserver.c hacking
    MySQL(六)常用语法和数据类型
    MySQL(五)汇总和分组数据
    MySQL(四)字段及常用函数
    MySQL(三)用正则表达式搜索
    MySQL(二)数据的检索和过滤
  • 原文地址:https://www.cnblogs.com/sunshine-2015/p/6024620.html
Copyright © 2011-2022 走看看