jvm-多线程
多线程的目的
为什么要使用多线程?可以简单的分两个方面来说:
- 在多个cpu核心下,多线程的好处是显而易见的,不然多个cpu核心只跑一个线程其他的核心就都浪费了;
- 即便不考虑多核心,在单核下,多线程也是有意义的,因为在一些操作,比如IO操作阻塞的时候,是不需要cpu参与的,这时候cpu就可以另开一个线程去做别的事情,等待IO操作完成再回到之前的线程继续执行即可。
多线程带来的问题其实多线程根本的问题只有一个:线程间变量的共享
java里的变量可以分3类:
1类变量(类里面static修饰的变量)
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步操作:
- 把主内存里的a复制到线程的工作内存
- 线程对工作内存里的a执行a=a+1
- 把线程工作内存里的a同步回主内存
而5个线程并发执行的时候完全有可能5个线程都先执行了第一步,这样5个线程的工作内存里a的初始值都是0,然后执行a=a+1后在工作内存里的运算结果都是1,最后同步回主内存的值肯定也是1。
而避免这种情况的方法就是:在多个线程并发访问a的时候,保证a在同一个时刻只被一个线程使用。
同步(synchronized)就是:在多个线程并发访问共享数据的时候,保证共享数据在同一个时刻只被一个线程使用。
同步基本思想
为了保证共享数据在同一时刻只被一个线程使用,我们有一种很简单的实现思想,就是 在共享数据里保存一个锁 ,当没有线程访问时,锁是空的,当有第一个线程访问时,就 在锁里保存这个线程的标识 并允许这个线程访问共享数据。在当前线程释放共享数据之前,如果再有其他线程想要访问共享数据,就要 等待锁释放 。
我们把这种思想的三个关键点抽出来:
- 在共享数据里保存一个锁
- 在锁里保存这个线程的标识
- 其他线程访问已加锁共享数据要等待锁释放
jvm-多线程的更多相关文章
- 2019百度阿里Java面试题(基础+框架+数据库+分布式+JVM+多线程)
前言 很多朋友对面试不够了解,不知道如何准备,对面试环节的设置以及目的不够了解,因此成功率不高.通常情况下校招生面试的成功率低于1%,而社招的面试成功率也低于5%,所以对于候选人一定要知道设立面试的初 ...
- 70道阿里百度高频Java面试题(框架+JVM+多线程+算法+数据库)
基础与框架 1.String类能被继承吗,为什么 2.String,Stringbuffer,StringBuilder的区别? 3.ArrayList和LinkedList有什么区别 4.类的实例化 ...
- 多线程概述(好处和弊端)(jvm多线程解析、主线程运行示例)
1 package multithread; 2 3 /* 4 * 进程:正在进行中的程序(直译). 5 * 6 * 线程:就是进程中一个负责程序执行的控制单元(执行路径). 7 * 一个进程中可以多 ...
- Java JVM多线程
- Java基础之多线程
1.进程和线程: 进程:正在进行的程序.每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元. 线程:进程内部的一条执行路径或者一个控制单元. 两者的区别: 一个进程至少有一个线程 ...
- JVM内存简单理解
1.首先简单说一下CPU与内存之间的关系 CPU运转速度快,磁盘的读写速度远远不及CPU运转速度,所以设计了内存来缓冲CPU等待磁盘读写:随着CPU的发展,内存读写也远远跟不上CPU的读写速度,CPU ...
- Java基础:Java虚拟机(JVM)
当我们第一次学习Java时这些原理上的东西就会被提到,但是很少有真正去学习.今天开始从头过一遍Java,打算从JVM开始. 1. JVM是什么 2. JRE和JDK 3. JVM结构 3.1. 程序计 ...
- JVM内存结构/JVM运行时数据区,以及堆内存的划分
1.程序计数器: 程序计数器是线程私有的内存,JVM多线程是通过线程轮流切换并分配处理器执行时间的方式实现的,当线程切换后需要恢复到正确的执 行位置(处理器)时,就是通过程序计数器来实现的.此内存区域 ...
- 软帝学院:java多线程知识点分享
1.进程和线程: 进程:正在进行的程序.每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元. 线程:进程内部的一条执行路径或者一个控制单元. 两者的区别: 一个进程至少有一个线程 ...
- 二、运行时JVM结构组成及作用
二.运行时JVM结构组成及作用 程序计数器 是否共享:否,线程私有,每个线程有1个独立的程序计数器! 所处位置:线程私有的内部区域 生命周期:与线程绑定 主要作用: 当前线程执行字节码的行号指示器! ...
随机推荐
- 【转】Spring+Websocket实现消息的推送
本文主要有三个步骤 1.用户登录后建立websocket连接,默认选择websocket连接,如果浏览器不支持,则使用sockjs进行模拟连接 2.建立连接后,服务端返回该用户的未读消息 3.服务端进 ...
- 离线+生成树+并查集——cf1213G
#include<bits/stdc++.h> using namespace std; #define N 200005 #define ll long long struct Edge ...
- Delphi实现程序只运行一次并激活已打开的程序
我们的程序有时候只允许运行一次,并且最好的情况是,如果程序第二次运行,就激活原来的程序.网上有很多的方法实现程序只运行一次,但对于激活原来的窗口却都不怎么好.关键就在于激活原来的程序,一般的做法是在工 ...
- (转)NAT与NAT穿越学习总结--ICE过程讲的不错
转:http://cgs1999.iteye.com/blog/1994072 1.引言网络地址转换(Network Address Translation,简称NAT)是一种在IP分组通过路由器或防 ...
- spring AOP (使用AspectJ的xml方式 的aop实现) (7)
目录 一.定义计算器接口跟实现类 二.定义两个切面,日志切面和验证切面 三.在xml中配置切面 四.测试类 一.定义计算器接口跟实现类 public interface ArithmeticCalcu ...
- 工具类--map 转成xml xml转成map
public class WxChatReq { /** * Map转换成XML * @param data * @return * @throws Exception */ public stati ...
- python使用threading获取线程函数返回值的实现方法
python使用threading获取线程函数返回值的实现方法 这篇文章主要介绍了python使用threading获取线程函数返回值的实现方法,需要的朋友可以参考下 threading用于提供线程相 ...
- git分布式版本控制系统权威指南学习笔记(二):git add暂存区的三个状态以及暂存区的理解
文章目录 不经过git add(到暂存区),能直接进行commit吗? 举个
- super_blocks没有导出
在模块中,通过查询super_blocks列表,来遍历系统中的所有super_block,但是出现与下面类似的错误: http://stackoverflow.com/q/5051111/941650 ...
- UVA 1525 Falling Leaves
题目链接:https://vjudge.net/problem/UVA-1525 题目链接:https://vjudge.net/problem/POJ-1577 题目大意 略. 分析 建树,然后先序 ...