1. Java内存模型(Java Memory Model, JMM)
Java的内存模型如下,所有变量都存储在主内存中,每个线程都有自己的工作内存。
共享变量:如果一个变量在多个线程中都使用到了,那么这个变量就是这几个线程的共享变量。
可见性:一个线程对共享变量的修改,能够及时地到主内存并且让其他的线程看到。
怎么理解上面的可见性的意思呢?
线程对共享变量的修改,只能在自己的工作内存里操作,不能直接对主内存中的共享变量进行修改。而且一个线程不能直接访问另一个线程中的变量的值,只能通过主内存进行共享传递。
那么就要求线程A对共享变量修改后,及时地更新到主内存中,线程B才可以及时地从主内存获取最新的值到工作内存。
比如一个共享变量int i = 0; 线程A将其改为i =1; 其他线程此时获取i的值,应该能及时地得到1,而不是0。
2. synchronized实现可见性
synchronized除了常见的原子性,还实现了可见性。这是因为:
1) 线程解锁前,必须把共享变量的最新值刷新到主内存中去;
2) 线程加锁时,将清空工作内存中的共享变量的值,使用到共享变量时,从主内存中获取最新的共享变量值(加锁和解锁需要同一把锁)
3. volatile实现可见性
通过内存屏障和禁止重排序优化来实现可见性。
1) 对共享变量进行写操作后,加入一条store屏障指令,强制将共享变量的值刷新到主内存;
2) 对共享变量进行读操作前,加入一条load屏障指令,强制从主内存中将最新值刷新到工作内存;
4.volatile不能保证原子性
一个比较典型的例子是++运算符。
在下面的代码中,一共创建了1000个线程,预期应该是加了1000次,那么number的值应该是1000,实际上有可能并不是。
这是因为,++运算符并不是一次操作。以number++为例,可以看作是,先从主内存中取出number的值,然后将其加1,刷新工作内存,刷新主内存,这么几个步骤。
而volatile并不能保证原子性,这就意味着,有可能出现这种情况:
1)线程A获取到主内存的number的值(假设为10)到工作内存
2)此时CPU调度,A暂停,线程B开始执行,同样从主内存中获取到number为10,number++后,number为11,刷新到主内存
3)线程A继续执行number++,它的工作内存中number为10,执行完毕刷新到主内存,此时,number的值为11. 也就是说,AB两个线程同时进行了+1操作,但最终的结果,只加了1
public class VolatileDemo { private int number = 0; public void increase() {
number++;
} public int getNumber() {
return number;
} public static void main(String[] args) {
final VolatileDemo demo = new VolatileDemo(); for (int i = 0; i <= 999; i++) {
new Thread(new Runnable() {
@Override
public void run() {
demo.increase();
}
}).start();
} //线程未执行完,主线程让出CPU资源
while(Thread.activeCount() > 1){
Thread.yield();
} //待上面的线程都执行完了,再打印,避免打印的不是最后的数据
System.out.println(demo.getNumber());
}
}
5.volatile适用场景
1)对共享变量的写操作,不依赖于其之前的值
不合适:number++, number = number * 2, number += 1等
合适:boolean值
2)该变量没有包含在具有其他变量的不变式中,也就是说,不同的volatile变量之间,不能互相依赖
6.AtomicInteger实现递增
上面我们已经知道一个整型的共享变量要实现递增,如果使用++运算符,即使加上volatile关键字,也是无法保证其原子性的。而如果在访问变量时加上synchronized块,或者可重入锁,开销又太大。
JDK1.5之后,可以使用AtomicInteger进行递增。该类是线程安全的。
将上面的代码修改如下,就可以保证原子性和可见性。
import java.util.concurrent.atomic.AtomicInteger; public class VolatileDemo { private AtomicInteger number = new AtomicInteger(0); public void increase() {
number.incrementAndGet();
} public int getNumber() {
return number.intValue();
} public static void main(String[] args) {
final VolatileDemo demo = new VolatileDemo(); for (int i = 0; i <= 999; i++) {
new Thread(new Runnable() {
@Override
public void run() {
demo.increase();
}
}).start();
} //线程未执行完,主线程让出CPU资源
while(Thread.activeCount() > 1){
Thread.yield();
} //待上面的线程都执行完了,再打印,避免打印的不是最后的数据
System.out.println(demo.getNumber());
}
}
【慕课网学习笔记】Java共享变量的可见性和原子性的更多相关文章
-
JavaScript进阶--慕课网学习笔记
JAVASCRIPT—进阶篇 给变量取个名字(变量命名) 变量名字可以任意取,只不过取名字要遵循一些规则: 1.必须以字母.下划线或美元符号开头,后面可以跟字 ...
-
JavaScript入门--慕课网学习笔记
JAVASCRIPT—(慕课网)入门篇 我们来看看如何写入JS代码?你只需一步操作,使用<script>标签在HTML网页中插入JavaScript代码.注意, <script&g ...
-
HTML基本语法(慕课网学习笔记)
标题 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8& ...
-
初识javaScript(慕课网学习笔记)
js输出 window.alert() 警告框 document.write() 写到HTML文档中 innerHTML 写到HTML元素 console.log() 写到浏览器的控制台 <!D ...
-
Float浮动(慕课网学习笔记)
float浮动 属性:值 意义 float:left 左浮动 float:right 右浮动 float:none 不浮动 float:inherit 继承父元素浮动属性,若父元素没有浮动属性则失效 ...
-
CSS基本语法(慕课网学习笔记)
CSS的声明,内外联样式以及CSS的优先级 css学习.html <!DOCTYPE html> <html lang="en"> <head> ...
-
js之window对象(慕课网学习笔记)
javaScript定义了一个变量一个函数都会变成window中的一个成员 var a=1; alert(window.a) //会输出a的值 window基础 创建窗口.调整窗口.移动窗口.关闭窗口 ...
-
js之DOM入门(慕课网学习笔记)
DOM简介 获得元素 document.getElementById('') 1.通过id获得元素内容 document.getElementsByTagName('') 2.通过标签获得元素内容 d ...
-
CSS定位(慕课网学习笔记)
定位模型 static自然模型 relative相对定位模型 absolute绝对定位模型 fixed固定定位模型 sticky磁铁定位模型 possition之static(默认的设置)(静态定位. ...
随机推荐
-
http升https笔记
证书申请的: 1.lets encrypt 国际公益项目发展很快的,不过在国内暂时有些支持度还不够高,如微信安卓版就不认lets encrypt的证书.跳转进去一直处于空白页状态 2.沃通证书 国内 ...
-
深入理解IOC模式及Unity框架
研究了下,有几篇博客确实已经说得很清楚了 1.IoC模式:http://www.cnblogs.com/qqlin/archive/2012/10/09/2707075.html 这篇博客是通过一个 ...
-
ubuntu安装ftp服务器
ubuntu安装ftp服务器 1: 安装vsftpd ~$ sudo apt-get install vsftpd ubuntu10.10自己装了,这步省略. 2: 配置vsftpd 2.1 修改vs ...
-
PID控制器的数字实现及C语法讲解
PID控制器的数字实现及C语法讲解 概述 为方便学习与交流,根据自己的理解与经验写了这份教程,有错误之处请各位读者予以指出,具体包含以下三部分内容: (1) PID数字化的推导过程(实质:微积分的近 ...
-
Matlab中plot、fplot、ezplot的使用方法和区别
函数plot 是绘制二维图形的最基本函数,它是针对向量或矩阵的列来绘制曲线的.也就是说,使用plot 函数之前,必须首先定义好曲线上每一点的x 及y 坐标; 常用格式为: (1)plot(x) 当x ...
-
[wikioi]乌龟棋
http://wikioi.com/problem/1068/ 多重背包.边界f[0,0,0,0]=a[1](初始时没有用任何卡片,获得棋盘第一格的分数)DP方程:f[i,j,k,l]=max(f[i ...
-
Delphi中动态创建的Panel无法改变颜色的解决办法(要把Panel的ParentBackground设为False)
刚开始代码如下: procedure TForm1.Button1Click(Sender: TObject); var Panel: TPanel; begin Panel := TPanel.Cr ...
-
linux网络编程:三次握手与四次挥手
建立TCP需要三次握手才能建立,而断开连接则需要四次握手.整个过程如下图所示: 其中三次握手即建立连接 四次挥手则为关闭连接 TCP连接的11种状态 客户端独有的:(1)SYN_SENT (2)FIN ...
-
javascript中三目运算符和if else有什么区别
javascript中三目运算符和if else有什么区别今天写了一个图片轮播的小demo,用到了判断先试了一下if else,代码如下:if(n >= count-1){n =0;}else{ ...
-
java Socket多线程聊天程序
参考JAVA 通过 Socket 实现 TCP 编程 参考java Socket多线程聊天程序(适合初学者) 以J2SDK-1.3为例,Socket和ServerSocket类库位于java.net包 ...