zoukankan      html  css  js  c++  java
  • JVM保证线程安全

     线程安全

    Java内存模型中,程序(进程)拥有一块内存空间,可以被所有的线程共享,即MainMemory(主内存);而每个线程又有一块独立的内存空间,即WorkingMemory(工作内存)。普通情况下,当线程需要对某一共享变量进行修改时,通常会进行如下的过程:

    1.      从主内存中拷贝变量的一份副本,并装载到工作内存中;

    2.      在工作内存中执行代码,修改副本的值;

    3.      用工作内存中的副本值更新主存中的相关变量值。

    如下图:

    所谓“线程安全”,即多个线程同时执行同一段代码时,不会出现不确定的或者与单线程条件下不一致的结果。通常,下列三种条件居其一的并发访问被JVM认为是线程安全的:

    1.      有final关键字修饰且已被赋值;

    2.      有volatile关键字修饰;

    3.      有锁保护(synchronized、ReentrantLock等)。

    第1点显而易见,不再赘述。

    volatile关键字的作用是告知JVM:它所修饰的域的原子操作都不需要经过线程的工作内存,而直接在主内存中进行修改。这样就保证了线程从主内存中读取(read)它的值的时候,总是最新的。但是,Java中的运算极少是原子的,即便是像++ 这样的一元运算符或者+= 这样的二元运算符都不是原子的,因此volatile关键字修饰的域在多线程环境下依然可能会读写出“脏”数据:它只保证每一步原子操作的线程安全,但不保证整个操作过程的线程安全。也因此,volatile主要被用于变量只有原子操作的场合,如赋值、移位等。

    锁,无论是显式(ReentrantLock)还是隐式(synchronized)的同步锁,或是信号量(Semaphore),抑或是阻塞队列(BlockingQueue),还是其它的同步措施(CyclicBarrier、CountDownLatch、wait&notify等),它们的作用都是一样的,就是保证一个共享变量的副本进入到某个线程的工作内存之后,该共享变量就不再会被其它线程访问到,直到前述过程的第3步执行完成。

    线程在有同步锁的情况下访问共享变量的过程如下:

    1.      获取同步锁

    2.      清空工作内存

    3.      从主内存将拷贝变量副本,并装载到工作内存

    4.      对副本执行代码

    5.      用副本数据更新主内存中的相关变量

    6.      释放同步锁

    通常,没有获得同步锁的线程将被阻塞,直到它竞争到同步锁。这样,没有获得同步锁的线程不仅不能访问数据,甚至都不能继续运行,于是强迫性地保证了线程安全。也因此,线程安全代码的开销要大于不安全的代码,同步锁的开销也要大于volatile。

  • 相关阅读:
    SpringMVC视图解析器
    FreeMarker介绍
    一篇很全面的freemarker教程
    request,session,application
    nav布局 在线演示 DIVCSS5
    opacity
    java 某字符串在另一字符串中是否存在
    bochs 使用讲解
    使用VS2015搭建Lua开发环境
    Zip文件格式
  • 原文地址:https://www.cnblogs.com/-wyl/p/6234563.html
Copyright © 2011-2022 走看看