线程安全的本质

什么是线程安全

要说什么是线程安全,那么我们看看生活中例子吧。

场景1:王菲要来西安体育场开演唱会,这个体育场规模不是很大,能容纳1000个人,于是准备了1000张票,后天中午12点在售票平台准时放票。很多粉丝等着一天等了很久,于是12点一到,大家迅速登录开始抢票,结果2000个人抢到了票,超卖了1000张,那么完了,到时候你让这些粉丝做哪里呢,势必会引发争端,甚至有可能发生踩踏事件,后果非常严重。在这个过程中,我们每一个粉丝点一次抢票按钮,就相当于有一个线程去帮我买做了买票的事情。最后发生了超卖,就是线程不安全

场景2:春运到了,在外漂泊多年的你,准备今年无论如何要回家看看爸爸妈妈。于是你在售票开始的那一天,登上12306抢到了一张西安发往兰州的G55列车5车厢2F座的票。腊月29收拾好行囊,买了一些当地的特产,带上给家人的礼物,踏上了回家的路,结果到了车上,发现你的座位已经有人了,他的车票和你的车票信息一模一样。在这个过程中,每点击一次买票动作,也就有相当于有一个线程帮我做了买票下单的操作,最终发生了车票被重复售卖,这也是线程不安全

总结上面了例子,我们发现两例线程不安全的例子,都有一个共同的特点,那就是操作了共享数据。无论是1000个体育场的座位,还是回家的车票,都可以算是共享数据。因此线程安全的核心点是共享数据。如果你有一天在自己家里将水管弄破了,把灯泡闪了,会不会有人说你,有可能会,那你大可以回他一句,滚,与你何干。对,与你何干。你为什么有底气说出这句话,那是因为这是你的私人财产,我想怎么样就怎么样,别人管不着。因此什么时候不会有线程安全的问题?那就是每个线程操作自己私有的数据,则不会发生线程安全的问题。

所以要搞清楚线程什么时候安全,什么时候不安全,我们首先要搞清楚,什么是线程共享数据,什么是线程私有数据?

JVM中线程共享的数据

关于这个问题,我们这里推荐阅读:[Run-Time Data Areas](The Java® Virtual Machine Specification (oracle.com)),这里将部分内容摘抄下来做简单的翻译,只涉及到和我们今天主题相关的堆、栈等。

Run-Time Data Areas:运行时数据区

The Java Virtual Machine defines various run-time data areas that are used during execution of a program. Some of these data areas are created on Java Virtual Machine start-up and are destroyed only when the Java Virtual Machine exits. Other data areas are per thread. Per-thread data areas are created when a thread is created and destroyed when the thread exits.

Java虚拟机定义了在程序执行期间使用的各种各样的运行时数据区。其中一些数据区在Java虚拟机启动的时候被创建,在Java虚拟机退出的时候被销毁。其他的运行时数据区是属于每个线程的,他们在线程创建的时候被创建,在线程被退出的时候被销毁。

Heap:堆

The Java Virtual Machine has a heap that is shared among all Java Virtual Machine threads. The heap is the run-time data area from which memory for all class instances and arrays is allocated. The heap is created on virtual machine start-up. Heap storage for objects is reclaimed by an automatic storage management system (known as a garbage collector); objects are never explicitly deallocated. The Java Virtual Machine assumes no particular type of automatic storage management system, and the storage management technique may be chosen according to the implementor’s system requirements. The heap may be of a fixed size or may be expanded as required by the computation and may be contracted if a larger heap becomes unnecessary. The memory for the heap does not need to be contiguous. A Java Virtual Machine implementation may provide the programmer or the user control over the initial size of the heap, as well as, if the heap can be dynamically expanded or contracted, control over the maximum and minimum heap size. The following exceptional condition is associated with the heap: • If a computation requires more heap than can be made available by the automatic storage management system, the Java Virtual Machine throws an OutOfMemoryError.

Java虚拟机的堆是所有线程共享的,在虚拟机启动的时候被创建。所有类的实例和数组的内存都是在堆上进行分配的,堆上的对象从来不会被显示的进行回收,GC会帮助我们自动做这件事。堆的大小可以是固定的,如果不够用的话,也可以自己去通过计算得到一个合适的值,做堆区的扩展。当然如果你觉得用不了,那么也可以进行回收。堆区的空间可以不用是连续的。java虚拟机的实现允许用户程序员或者用户控制堆的大小,以及如果堆可以进行堆的扩展或者收缩,还可以控制堆的最大最小空间。如果程序计算需要的堆空间比虚拟家自动分配的空间大,那么虚拟机会抛出一个异常:OutOfMemoryError,也就是我们说的OOM。

Method Area:方法区

The Java Virtual Machine has a method area that is shared among all Java Virtual Machine threads. The method area is analogous to the storage area for compiled code of a conventional language or analogous to the “text” segment in an operating system process. It stores per-class structures such as the run-time constant pool, field and method data, and the code for methods and constructors, including the special methods (§2.9) used in class and instance initialization and interface initialization. The method area is created on virtual machine start-up. Although the method area is logically part of the heap, simple implementations may choose not to either garbage collect or compact it. This specification does not mandate the location of the method area or the policies used to manage compiled code. The method area may be of a fixed size or may be expanded as required by the computation and may 2.5 Run-Time Data Areas THE STRUCTURE OF THE JAVA VIRTUAL MACHINE 14 be contracted if a larger method area becomes unnecessary. The memory for the method area does not need to be contiguous. A Java Virtual Machine implementation may provide the programmer or the user control over the initial size of the method area, as well as, in the case of a varying-size method area, control over the maximum and minimum method area size. The following exceptional condition is associated with the method area: • If memory in the method area cannot be made available to satisfy an allocation request, the Java Virtual Machine throws an OutOfMemoryError.

方法区也是所有线程共享的,在虚拟机启动的时候被创建,方法区类似于传统编程语言编译代码的存储区域或者操作系统中的文本段(这句不是很理解)。方法区存储每个类的结构,比如运行时常量池类的属性和方法数据以及方法和构造函数的代码,包括类进行实例初始化和接口初始化的时候用到的一些特殊方法。方法区在逻辑上是属于heap的,简单的实现可以选择不进行垃圾回收。方法区的大小可以是固定的,也可以根据计算的需要进行扩展和回收,如果用不到那么大的方法区,Java14可能会考虑进行回收。方法区的可以是不连续的。Java虚拟机允许程序员或用户控制方法区的初始大小,在方法区大小可变的情况下,还可以控制方法区的最大和最小容量。如果方法区的内存不满足分配请求,也会抛出OOM的异常

Java Virtual Machine Stacks:栈

Each Java Virtual Machine thread has a private Java Virtual Machine stack, created at the same time as the thread. A Java Virtual Machine stack stores frames (§2.6). A Java Virtual Machine stack is analogous to the stack of a conventional language such as C: it holds local variables and partial results, and plays a part in method invocation and return. Because the Java Virtual Machine stack is never manipulated directly except to push and pop frames, frames may be heap allocated. The memory for a Java Virtual Machine stack does not need to be contiguous. In the First Edition of The Java® Virtual Machine Specification, the Java Virtual Machine stack was known as the Java stack. This specification permits Java Virtual Machine stacks either to be of a fixed size or to dynamically expand and contract as required by the computation. If the Java Virtual Machine stacks are of a fixed size, the size of each Java Virtual Machine stack may be chosen independently when that stack is created. A Java Virtual Machine implementation may provide the programmer or the user control over the initial size of Java Virtual Machine stacks, as well as, in the case of dynamically expanding or contracting Java Virtual Machine stacks, control over the maximum and minimum sizes. The following exceptional conditions are associated with Java Virtual Machine stacks: • If the computation in a thread requires a larger Java Virtual Machine stack than is permitted, the Java Virtual Machine throws a StackOverflowError. • If Java Virtual Machine stacks can be dynamically expanded, and expansion is attempted but insufficient memory can be made available to effect the expansion, or if insufficient memory can be made available to create the initial Java Virtual Machine stack for a new thread, the Java Virtual Machine throws an OutOfMemoryError.

每一个线程有一个私有的Java虚拟机栈,他和线程同时被创建。Java虚拟机栈类似于传统编程语言中的栈,比如C语言。他用来保存局部变量和部分结果,并且在方法的调用和返回的时候发挥一定作用(这块应该主要说程序计数器用来保存程序的执行状态、执行大代码的位置等)。栈的空间不需要连续,在第一版Java规范中Java虚拟机栈也被叫做java栈。栈可以是固定大小的,也可以根据需要动态的进行扩展或者缩小。如果Java虚拟机堆栈的大小是固定的,则每个Java虚拟机栈的大小可以在创建时确定。Java虚拟机实现可以让程序员或用户控制Java虚拟机堆栈的初始大小,在动态扩展或收缩Java虚拟机堆栈的情况下,还可以控制最大和最小容量。以下是和Java虚拟机栈有关的两个异常:如果一个线程需要的栈比虚拟机能分配给我的大,那么抛出StackOverflowError异常。如果要动态的扩展栈空间,但是没有足够的大小,那么则会抛出OOM的异常。

对上面关于Java运行时数据区做以下简单的总结:

  1. 实例化的对象、数组分配的内存被放在堆区,是线程共享的。堆区是垃圾回收的主要区域
  2. 类的属性(static修饰的属性是属于类的)、运行时常量池是放在方法区的,是线程共享的
  3. 方法中的局部变量是放在栈区的,每个方法会加载自己私有的线程栈,这个是线程私有的。

到这里我们就解决本文最初最初提出的问题?什么是共享数据、什么是私有数据。

并发编程02-什么是线程安全以及Java虚拟机中哪些数据是线程共享的,那些是线程私有的相关推荐

  1. 并发编程-02并发基础CPU多级缓存和Java内存模型JMM

    文章目录 CPU多级缓存 CPU多级缓存概述 CPU 多级缓存-缓存一致性协议MESI CPU 多级缓存-乱序执行优化-重排序 JAVA内存模型 (JMM) 计算机硬件架构简易图示 JAVA内存模型与 ...

  2. 【Java并发】Java并发编程-02

    1.Java并发包(JUC) Java核心库的包 并发包分类 Java.util.concurrency 锁机制类 Locks : Lock, Condition, ReadWriteLock 原子操 ...

  3. concurrent 底层_万字长文!从底层开始带你了解并发编程,彻底帮你搞懂Java锁!

    线程是否要锁住同步资源 锁住 悲观锁 不锁住 乐观锁 锁住同步资源失败 线程是否要阻塞 阻塞 不阻塞自旋锁,适应性自旋锁 多个线程竞争同步资源的流程细节有没有区别 不锁住资源,多个线程只有一个能修改资 ...

  4. java并发编程3:使用JDK并发包(java.util.concurrent)构建程序

    原文地址为: java并发编程3:使用JDK并发包(java.util.concurrent)构建程序 java.util.concurrent 概述 JDK5.0 以后的版本都引入了高级并发特性,大 ...

  5. c++指定在某一线程运行_深入理解Java虚拟机-运行时数据区

    在Java虚拟机的概念中,运行时数据区又被称为Java内存区域.主要由线程私有的程序计数器.虚拟机栈和本地方法栈以及线程共享的堆和方法区组成. 程序计数器 程序计数器在Java内存中占据的空间比较小, ...

  6. Java并发编程(02):线程核心机制,基础概念扩展

    本文源码:GitHub·点这里 || GitEE·点这里 一.线程基本机制 1.概念描述 并发编程的特点是:可以将程序划分为多个分离且独立运行的任务,通过线程来驱动这些独立的任务执行,从而提升整体的效 ...

  7. 多线程进阶=》JUC并发编程02

    在JUC并发编程01中说到了,什么是JUC.线程和进程.Lock锁.生产者和消费者问题.8锁现象.集合类不安全.Callable(简单).常用辅助类.读写锁 https://blog.csdn.net ...

  8. 实战并发编程 - 02解决并发问题常用套路

    文章目录 Pre 解决并发问题的方法 无锁的方式解决并发问题 局部变量 不可变对象 ThreadLocal CAS原子类 有锁的方式解决并发问题 ReentrantLock可重入锁 synchroni ...

  9. JUC并发编程02——AQS源码剖析

    1.AQS介绍 相信每个Java Coder 都使用过或者至少听说过AQS, 它是抽象队列同步器AbstractQueuedSynchronizer 的简称,在juc包下.它提供了一套可用于实现锁同步 ...

最新文章

  1. mysql在线上建索引,mysql 5.6在线DDL建索引测试
  2. 宏基因组公众号创立初衷及如何注册一个名字好记的公众号
  3. 昼猫笔记 JavaScript -- 异步执行 | 定时器真的定时执行?
  4. python十六:lambda匿名函数
  5. python flask 方法get_json返回(Ellipsis, Ellipsis)问题解决
  6. .Net Discovery系列之十二-深入理解平台机制与性能影响(下)
  7. 奇异值分解讨论及其实现的计算步骤
  8. WebClient UI忽略所有增强的开关
  9. cuba 平台_CUBA 7的新功能
  10. 二进制搜索算法_二进制搜索的一个扭曲故事
  11. sql always on_Always On可用性组中具有发布者数据库SQL复制
  12. bzoj2437 [Noi2011]兔兔与蛋蛋
  13. matlab仿真数字电路,基于matlab的数字逻辑电路仿真
  14. Qt视频播放器界面Demo
  15. 花瓣网画板上多张图片如何快速复制保存到
  16. 【Spring练习】Spring+SpringMVC+JdbcTemplate简单练习用户管理
  17. 运行 Android 的笔记本 Cosmo 已众筹超 130 万美元
  18. Monte Carlo analysis
  19. matlab实现iou计算,python实现IOU计算案例
  20. vue 移动端视频功能实现

热门文章

  1. 哪些域名才是真正的精品域名?
  2. C语言之循环while,do-while,for的简单使用方法及区别
  3. linux环境下车牌识别验证
  4. Vue使用高德地图api实现热力图动态缩放
  5. pytorch图像分类篇:6. ResNet网络结构详解与迁移学习简介
  6. 大好河山集团董事长黄国林受邀出席2023中国好公司高峰论坛暨产学研合作峰会
  7. 永磁同步电机矢量控制(八)——弱磁控制(直接计算法)
  8. win10激活的方法
  9. Java/JDK安装与环境配置教程
  10. eclipse中安装Subclipse插件