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个独立的程序计数器! 所处位置:线程私有的内部区域 生命周期:与线程绑定 主要作用: 当前线程执行字节码的行号指示器! ...
随机推荐
- TxtUtil
/** *包名:cn.yufu.utils *描述:package cn.yufu.utils; */ package cn.yufu.utils; import java.io.BufferedRe ...
- vue基础五
条件渲染 1.v-if 1.1<template>中v-if条件组 因为 v-if 是一个指令,需要将它添加到一个元素上.但是如果我们想切换多个元素呢?此时我们可以把一个<templ ...
- DNS域名服务器的搭建
父域的DNS(svr7): 可以解析父域名下保存的域名地址,即解析.tedu.cn下的域名 一.安装软件包bind.bind-chroot bind是DNS解析服务需要用到的服务软件包,bind- ...
- flink详细介绍
Flink是什么 Flink是一个分布式计算引擎 MapReduce Spark Storm 同时支持流计算和批处理 和Spark不同, Flink是使用流的思想做批, Spark是采用做批的思想做流 ...
- 剑指offer---1、顺时针打印矩阵
剑指offer---1.顺时针打印矩阵 一.总结 一句话总结: 谋而后动+多做:还是要谋而后动,但是怎么谋而后动,很有学问,做好的方式就是多做 问题就这些问题:解决了就好了,比如php多维数组 面试的 ...
- css 导航样式
html 结构 <div class="nav-menu float-r"> <ul class="menu-item"> <l ...
- jenkins的api操作
jenkins-client 实现方式 jenkins-client 底层是使用 HttpClient HttpPost 发送post 请求实现的,是将 REST API 封装了一下 添加依赖 < ...
- 使用redis实现客户端和服务端token验证
实在是思维江化啊,没有想到可以给redis设置不同的key值来实现不同key值存储不同的value值,而一直想着给一个名为token的key值新增不同的数据,并设置过期时间,然而这样却不能新增只能做到 ...
- PHP简单读取XML
DOM document object model,文档对象模型,就是树形结构 节点的分类 元素节点 属性节点 值节点 注释节点 根节点(document才是根节点) 生成XML文档 xml的根节点为 ...
- socket API CSocket CAsyncSocket 用法及区别
要进行网络编程就要和Socket打交道,Socket有同步阻塞方式和异步非阻塞方式两种使用,事实上同步和异步在我们编程的生涯中可能遇到了很多,而Socket也没什么特别.虽然同步好用,不费劲,但不能满 ...