多线程的目的

为什么要使用多线程?可以简单的分两个方面来说:

  • 在多个cpu核心下,多线程的好处是显而易见的,不然多个cpu核心只跑一个线程其他的核心就都浪费了;
  • 即便不考虑多核心,在单核下,多线程也是有意义的,因为在一些操作,比如IO操作阻塞的时候,是不需要cpu参与的,这时候cpu就可以另开一个线程去做别的事情,等待IO操作完成再回到之前的线程继续执行即可。

多线程带来的问题其实多线程根本的问题只有一个:线程间变量的共享

java里的变量可以分3类:

1类变量(类里面static修饰的变量)

2实例变量(类里面的普通变量)

3局部变量(方法里声明的变量)

根据各个区域的定义,我们可以知道:

  1. 类变量 保存在“方法区”
  2. 实例变量 保存在“堆”
  3. 局部变量 保存在 “虚拟机栈”

“方法区”和“堆”都属于线程共享数据区,“虚拟机栈”属于线程私有数据区。

因此,局部变量是不能多个线程共享的,而类变量和实例变量是可以多个线程共享的。事实上,在java中,多线程间进行通信的唯一途径就是通过类变量和实例变量。

也就是说,如果一段多线程程序中如果没有类变量和实例变量,那么这段多线程程序就一定是线程安全的。

以Web开发的Servlet为例,一般我们开发的时候,自己的类继承HttpServlet之后,重写doPost()、doGet()处理请求,不管我们在这两个方法里写什么代码,只要没有操作类变量或实例变量,最后写出来的代码就是线程安全的。如果在Servlet类里面加了实例变量,就很可能出现线程安全性问题,解决方法就是把实例变量改为ThreadLocal变量,而ThreadLocal实现的含义就是让实例变量变成了“线程私有”的,即给每一个线程分配一个自己的值。

现在我们知道: 其实多线程根本的问题只有一个:线程间变量的共享, 这里的变量,指的就是类变量和实例变量,后续的一切,都是为了解决类变量和实例变量共享的安全问题。

如何安全的共享变量

现在唯一的问题就是要让多个线程安全的共享变量(下文中的变量一般特指类变量和实例变量),上文提到了一种ThreadLocal的方式,其实这种方式并不是真正的共享,而是为每个线程分配一个自己的值。

比如现在有一个特别简单的需求,有一个类变量a=0,现在启动5个线程,每个线程执行a++;如果用ThreadLocal的方式,最后的结果就是5个线程都拥有一份自己的a值,最终结果都是1,这显然不符合我们的预期。

那么如果不使用ThreadLocal呢?直接声明一个类变量a=0,然后让5个线程分别去执行a++;这样结果依旧不对,而且结果是不确定的,可能是1,2,3,4,5中的任一个。这种情况叫做竞态条件(Race Condition),要理解竞态条件先要理解Java内存模型:

要理解java的内存模型,可以类比计算机硬件访问内存的模型。由于计算机的cpu运算速度和内存io速度有几个数量级的差距,因此现代计算机都不得不加入一层尽可能接近处理器运算速度的高速缓存来做缓冲:将内存中运算需要使用的数据先复制到缓存中,当运算结束后再同步回内存。如下图:

因为jvm要实现跨硬件平台,因此jvm定义了自己的内存模型,但是因为jvm的内存模型最终还是要映射到硬件上,因此jvm内存模型几乎与硬件的模型一样:

每个java线程都有一份自己的工作内存,线程访问变量的时候,不能直接访问主内存中的变量,而是先把主内存的变量复制到自己的工作内存,然后操作自己工作内存里的变量,最后再同步给主内存。

现在就可以解释为什么5个线程执行a++最后结果不一定是5了,因为a++可以分解为3步操作:

  1. 把主内存里的a复制到线程的工作内存
  2. 线程对工作内存里的a执行a=a+1
  3. 把线程工作内存里的a同步回主内存

而5个线程并发执行的时候完全有可能5个线程都先执行了第一步,这样5个线程的工作内存里a的初始值都是0,然后执行a=a+1后在工作内存里的运算结果都是1,最后同步回主内存的值肯定也是1。

而避免这种情况的方法就是:在多个线程并发访问a的时候,保证a在同一个时刻只被一个线程使用。

同步(synchronized)就是:在多个线程并发访问共享数据的时候,保证共享数据在同一个时刻只被一个线程使用。

同步基本思想

为了保证共享数据在同一时刻只被一个线程使用,我们有一种很简单的实现思想,就是 在共享数据里保存一个锁 ,当没有线程访问时,锁是空的,当有第一个线程访问时,就 在锁里保存这个线程的标识 并允许这个线程访问共享数据。在当前线程释放共享数据之前,如果再有其他线程想要访问共享数据,就要 等待锁释放 。

我们把这种思想的三个关键点抽出来:

  1. 在共享数据里保存一个锁
  2. 在锁里保存这个线程的标识
  3. 其他线程访问已加锁共享数据要等待锁释放

jvm-多线程的更多相关文章

  1. 2019百度阿里Java面试题(基础+框架+数据库+分布式+JVM+多线程)

    前言 很多朋友对面试不够了解,不知道如何准备,对面试环节的设置以及目的不够了解,因此成功率不高.通常情况下校招生面试的成功率低于1%,而社招的面试成功率也低于5%,所以对于候选人一定要知道设立面试的初 ...

  2. 70道阿里百度高频Java面试题(框架+JVM+多线程+算法+数据库)

    基础与框架 1.String类能被继承吗,为什么 2.String,Stringbuffer,StringBuilder的区别? 3.ArrayList和LinkedList有什么区别 4.类的实例化 ...

  3. 多线程概述(好处和弊端)(jvm多线程解析、主线程运行示例)

    1 package multithread; 2 3 /* 4 * 进程:正在进行中的程序(直译). 5 * 6 * 线程:就是进程中一个负责程序执行的控制单元(执行路径). 7 * 一个进程中可以多 ...

  4. Java JVM多线程

  5. Java基础之多线程

    1.进程和线程: 进程:正在进行的程序.每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元. 线程:进程内部的一条执行路径或者一个控制单元. 两者的区别: 一个进程至少有一个线程 ...

  6. JVM内存简单理解

    1.首先简单说一下CPU与内存之间的关系 CPU运转速度快,磁盘的读写速度远远不及CPU运转速度,所以设计了内存来缓冲CPU等待磁盘读写:随着CPU的发展,内存读写也远远跟不上CPU的读写速度,CPU ...

  7. Java基础:Java虚拟机(JVM)

    当我们第一次学习Java时这些原理上的东西就会被提到,但是很少有真正去学习.今天开始从头过一遍Java,打算从JVM开始. 1. JVM是什么 2. JRE和JDK 3. JVM结构 3.1. 程序计 ...

  8. JVM内存结构/JVM运行时数据区,以及堆内存的划分

    1.程序计数器: 程序计数器是线程私有的内存,JVM多线程是通过线程轮流切换并分配处理器执行时间的方式实现的,当线程切换后需要恢复到正确的执 行位置(处理器)时,就是通过程序计数器来实现的.此内存区域 ...

  9. 软帝学院:java多线程知识点分享

    1.进程和线程: 进程:正在进行的程序.每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元. 线程:进程内部的一条执行路径或者一个控制单元. 两者的区别: 一个进程至少有一个线程 ...

  10. 二、运行时JVM结构组成及作用

    二.运行时JVM结构组成及作用 程序计数器 是否共享:否,线程私有,每个线程有1个独立的程序计数器! 所处位置:线程私有的内部区域 生命周期:与线程绑定 主要作用: 当前线程执行字节码的行号指示器!  ...

随机推荐

  1. HTML标签分类总结

    块级元素和行内元素块级元素address - 地址blockquote - 块引用center - 举中对齐块dir - 目录列表div - 常用块级容易,也是css layout的主要标签dl - ...

  2. 基础课(三)实验串入OSPF协议和HSRP协议以及HSRP外部链路跟踪

    实验要求1: ,2,3,4分别是vlan10,20,30,40的网关(网关IP-192.168.X.254 /24)      对vlan10做HSRP热备   SW1做主网关,SW2做备份网关    ...

  3. python培训拾遗

    20140421 1. 三大利器: dir----列出所有内部方法 a=1 dir(a) 可以列出所有内部方法,就是带两个下划线的:带一个下划线的是普通方法 help---查看帮助 help(a.bi ...

  4. vue 和 react 常用包(插件、组件 或 工具)

    vue 和 react  都可以使用的包(只是 纯 js 功能的包) 1.qs : https://blog.csdn.net/sansan_7957/article/details/82227040 ...

  5. (抓)ubuntu下安装mysql --- 我主要参考的文章

    转:http://cycnet.blog.51cto.com/117809/812625 现在的软件越来越好安装,尤其是在ubuntu下安装软件,更是没有技巧,只需要在联网的情况下使用apt-get ...

  6. Ethenet: MAC PHY MII RMII

    https://www.cnblogs.com/liangxiaofeng/p/3874866.html 1. general 下图是网口结构简图.网口由CPU.MAC和PHY三部分组成.DMA控制器 ...

  7. 2019杭电多校第三场hdu6606 Distribution of books(二分答案+dp+权值线段树)

    Distribution of books 题目传送门 解题思路 求最大值的最小值,可以想到用二分答案. 对于二分出的每个mid,要找到是否存在前缀可以份为小于等于mid的k份.先求出这n个数的前缀和 ...

  8. SPOJ VFMUL - Very Fast Multiplication (FFT)

    题目链接:VFMUL - Very Fast Multiplication Description Multiply the given numbers. Input n [the number of ...

  9. 021_IO流

    IO流 I——input 输入 O——Output 输出 文件 File 所有文件类的父类 创建方式 File file = new File("文件路径"); 文件对象常用的方法 ...

  10. VC++ 2010 创建高级Ribbon界面详解(4)

    5.辅助控件 除了前面我们介绍的按钮,工具栏,编辑框等基本控件外,为了支持现代软件对丰厚的界面交互方式的要求,Visual Studio 2010还提供了很多其他的辅助控件,例如我们通常会用到的“上一 ...