JVM内存模型来解释多线程并发常见问题和volilate,final,ThreadLocal

本贴最后更新于 2988 天前,其中的信息可能已经物是人非
  • 多核CPU运行时,每个CPU都会有自己的Cache,同样jvm运行时,每个线程的私有栈在使用共享数据时,都会现将共享数据拷贝进栈顶进行运算,这份数据其实是副本,因此也同样存在多个线程修改一个内存单元的一致性问题。
  • JVM有自己的一套内存模型(Java memory model,JMM)。JDK1.2开始有,JDK1.5(JSR-133)才逐渐成熟。JMM中将JVM内存分为“主存”和“工作内存”。 
    • Main memory,主存,堆内存就是Java对象使用的区域,JMM中定义它为主存。多线程数据一致性问题多发生在这里。
    • working memory ,工作内存,栈空间内部应当包含局部变量,对象的引用,操作数栈,当前方法的常量池指针,当前方法的返回地址等信息,这块空间最接近CPU运算,也是每个线程的私有空间,当调用方法时将给该私有栈分配空间,方法返回时回收栈空间,
    • 工作内存和主存之间会采用read/write的方式进行通信,,当工作内存中的数据需要计算时,它会发生load/store操作,load通常是将本地变量推至栈顶,用来给cpu调度,而store就是将栈顶数据写入本地变量。
    • load操作发生在read之后,普通变量修改未必立即发生Store,但是发生store就会发生write操作。
  • 可见性问题,同一个对象,一个线程将该对象的属性修改后,另一个线程看不到该属性被修改的结果,或者是未必能马上看到,这种可用volatile修饰属性解决。
  • 指令重排序问题,Java编译器在编译Java代码时,对虚指令重排序,也可以是cpu对目标指令重排序,它们的目的是为了高效
public  boolean inited;
private  int a;
public void init(){
    int a = 11;
    inited = true;//实际运行时可能被重排,inited的赋值在a之前.多线程判断时就肯能出问题,inited为true但是a还没有赋值。
}


  • 4字节赋值问题,jvm中允许对一个非volilate的64位(8字节)变量赋值时,分解成两个32位(4字节)完成,并不是必须一次性完成。如果变量是long或double类型的数据,赋值给32位后,正好被另一个线程读取,那么读出来的数就是不可预见的结果。
  • 非安全的发布,对象的初始化需要时间,一个线程初始化a对象,另一个线程来使用时,可能a对象还没有初始操作,拿到的就是空指针,另一种就是,a对象没有初始化完成,这个对象空间可能已经创建, 但是内部属性的初始还要时间,这是由于JMM中并没有要求普通属性的赋值,必须发生在构造方法return语句之前。然后程序访问了还没有准备好的内容,一个数据再给多线程使用时,要保证线程安全很难,简单的数据可以用final或者用数据拷贝,或者返回不允许修改的代理如集合提供的unmodifiableList(List)。
  • volilate:誉为最轻量级的锁,volilate变量也会像普通变量那样从主存拷贝到各个线程中去操作,区别在于它要求实现storeLoad指令屏障(load前先store)。 
    • 第一个作用保证多线程中共享变量始终可见,但引用对象内部的属性不能保证。
    • JDK1.5后对volilate增强,要求对volilate变量操作时,前后的指令在某种情况下不允许重排序,达到指令级别的轻量级锁,还有就是之前的4字节问题,必须一次性赋值
  • final:JMM中要求final的初始化动作必须在构造方法return前完成。
  • 栈封闭:也就是线程操作的数据都是私有的,不会与其他线程共享。web容器提供给我们的request和response对象就是私有的,因为它不会被其他线程占用,线程绝对安全。想要达到栈封闭的使用数据,除了局部变量外,还可以使用ThreadLocal,它本身可以被多个线程共享使用,又可以达到线程安全的目的。 
  • 用Thread的 ThreadLocal.ThreadLocalMap全局属性,保存线程-数据的键值对。 
    用完后一定要remove,因为如果是线程池,那么线程的生命周期不可预测。
/**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     *  
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    /**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }
 /**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
  • 线程
    120 引用 • 111 回帖 • 3 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3169 引用 • 8208 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • blague

    👍 从这个角度解释我觉得比较清晰。

  • 其他回帖
  • someone

    是的[em00]

  • someone

    可以从jvm内存分区特性来分析线程安全问题的本质[em01] 刚刚写了一大堆结果提交失败,还把我的评论清空了。。。2次!!![em03]

  • someone

    评论悲剧了?

推荐标签 标签

  • Lute

    Lute 是一款结构化的 Markdown 引擎,支持 Go 和 JavaScript。

    25 引用 • 191 回帖 • 24 关注
  • CAP

    CAP 指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。

    11 引用 • 5 回帖 • 582 关注
  • WordPress

    WordPress 是一个使用 PHP 语言开发的博客平台,用户可以在支持 PHP 和 MySQL 数据库的服务器上架设自己的博客。也可以把 WordPress 当作一个内容管理系统(CMS)来使用。WordPress 是一个免费的开源项目,在 GNU 通用公共许可证(GPLv2)下授权发布。

    45 引用 • 113 回帖 • 284 关注
  • Typecho

    Typecho 是一款博客程序,它在 GPLv2 许可证下发行,基于 PHP 构建,可以运行在各种平台上,支持多种数据库(MySQL、PostgreSQL、SQLite)。

    12 引用 • 60 回帖 • 457 关注
  • 小说

    小说是以刻画人物形象为中心,通过完整的故事情节和环境描写来反映社会生活的文学体裁。

    28 引用 • 108 回帖
  • WiFiDog

    WiFiDog 是一套开源的无线热点认证管理工具,主要功能包括:位置相关的内容递送;用户认证和授权;集中式网络监控。

    1 引用 • 7 回帖 • 552 关注
  • Hadoop

    Hadoop 是由 Apache 基金会所开发的一个分布式系统基础架构。用户可以在不了解分布式底层细节的情况下,开发分布式程序。充分利用集群的威力进行高速运算和存储。

    84 引用 • 122 回帖 • 619 关注
  • HHKB

    HHKB 是富士通的 Happy Hacking 系列电容键盘。电容键盘即无接点静电电容式键盘(Capacitive Keyboard)。

    5 引用 • 74 回帖 • 422 关注
  • 黑曜石

    黑曜石是一款强大的知识库工具,支持本地 Markdown 文件编辑,支持双向链接和关系图。

    A second brain, for you, forever.

    10 引用 • 88 回帖
  • 30Seconds

    📙 前端知识精选集,包含 HTML、CSS、JavaScript、React、Node、安全等方面,每天仅需 30 秒。

    • 精选常见面试题,帮助您准备下一次面试
    • 精选常见交互,帮助您拥有简洁酷炫的站点
    • 精选有用的 React 片段,帮助你获取最佳实践
    • 精选常见代码集,帮助您提高打码效率
    • 整理前端界的最新资讯,邀您一同探索新世界
    488 引用 • 383 回帖 • 4 关注
  • 电影

    这是一个不能说的秘密。

    120 引用 • 597 回帖
  • 阿里云

    阿里云是阿里巴巴集团旗下公司,是全球领先的云计算及人工智能科技公司。提供云服务器、云数据库、云安全等云计算服务,以及大数据、人工智能服务、精准定制基于场景的行业解决方案。

    89 引用 • 345 回帖
  • Kubernetes

    Kubernetes 是 Google 开源的一个容器编排引擎,它支持自动化部署、大规模可伸缩、应用容器化管理。

    109 引用 • 54 回帖 • 2 关注
  • OpenResty

    OpenResty 是一个基于 NGINX 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

    17 引用 • 41 关注
  • 链滴

    链滴是一个记录生活的地方。

    记录生活,连接点滴

    140 引用 • 3720 回帖
  • 七牛云

    七牛云是国内领先的企业级公有云服务商,致力于打造以数据为核心的场景化 PaaS 服务。围绕富媒体场景,七牛先后推出了对象存储,融合 CDN 加速,数据通用处理,内容反垃圾服务,以及直播云服务等。

    26 引用 • 222 回帖 • 170 关注
  • B3log

    B3log 是一个开源组织,名字来源于“Bulletin Board Blog”缩写,目标是将独立博客与论坛结合,形成一种新的网络社区体验,详细请看 B3log 构思。目前 B3log 已经开源了多款产品:SymSoloVditor思源笔记

    1083 引用 • 3461 回帖 • 262 关注
  • BookxNote

    BookxNote 是一款全新的电子书学习工具,助力您的学习与思考,让您的大脑更高效的记忆。

    笔记整理交给我,一心只读圣贤书。

    1 引用 • 1 回帖 • 2 关注
  • LaTeX

    LaTeX(音译“拉泰赫”)是一种基于 ΤΕΧ 的排版系统,由美国计算机学家莱斯利·兰伯特(Leslie Lamport)在 20 世纪 80 年代初期开发,利用这种格式,即使使用者没有排版和程序设计的知识也可以充分发挥由 TeX 所提供的强大功能,能在几天,甚至几小时内生成很多具有书籍质量的印刷品。对于生成复杂表格和数学公式,这一点表现得尤为突出。因此它非常适用于生成高印刷质量的科技和数学类文档。

    9 引用 • 32 回帖 • 152 关注
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    165 引用 • 407 回帖 • 514 关注
  • Redis

    Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。从 2010 年 3 月 15 日起,Redis 的开发工作由 VMware 主持。从 2013 年 5 月开始,Redis 的开发由 Pivotal 赞助。

    284 引用 • 247 回帖 • 148 关注
  • jsoup

    jsoup 是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址、HTML 文本内容。它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出和操作数据。

    6 引用 • 1 回帖 • 476 关注
  • gRpc
    10 引用 • 8 回帖 • 51 关注
  • CentOS

    CentOS(Community Enterprise Operating System)是 Linux 发行版之一,它是来自于 Red Hat Enterprise Linux 依照开放源代码规定释出的源代码所编译而成。由于出自同样的源代码,因此有些要求高度稳定的服务器以 CentOS 替代商业版的 Red Hat Enterprise Linux 使用。两者的不同在于 CentOS 并不包含封闭源代码软件。

    238 引用 • 224 回帖
  • GraphQL

    GraphQL 是一个用于 API 的查询语言,是一个使用基于类型系统来执行查询的服务端运行时(类型系统由你的数据定义)。GraphQL 并没有和任何特定数据库或者存储引擎绑定,而是依靠你现有的代码和数据支撑。

    4 引用 • 3 回帖 • 26 关注
  • BAE

    百度应用引擎(Baidu App Engine)提供了 PHP、Java、Python 的执行环境,以及云存储、消息服务、云数据库等全面的云服务。它可以让开发者实现自动地部署和管理应用,并且提供动态扩容和负载均衡的运行环境,让开发者不用考虑高成本的运维工作,只需专注于业务逻辑,大大降低了开发者学习和迁移的成本。

    19 引用 • 75 回帖 • 618 关注
  • 996
    13 引用 • 200 回帖 • 2 关注