Java线程-volatile不能保证原子性
下面是一共通过volatile实现原子性的例子:
通过建立100个线程,计算number这个变量最后的结果。
package com.Sychronized;
public class VolatileDemo {
private volatile int number=0;
public int getNumber()
{
return this.number;
}
public void increase()
{
this.number++;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
final VolatileDemo volDemo=new VolatileDemo();
for(int i=0;i<100;i++)
{
new Thread(new Runnable() {
@Override
public void run() {
volDemo.increase();
}
}).start();
}
//如果还有子线程在运行,主线程就让出CPU资源
//直到所有的子线程都运行完了,主线程再继续往下执行
while(Thread.activeCount()>1)
{
Thread.yield();
}
System.out.println("number:"+volDemo.getNumber());
}
}
运行结果:
发现有几种结果:


造成这个结果的原因就是,volatile关键字具有可见性,number++实际上有三步操作,但是不具备原子性。
程序分析:
number++包含三步操作:1,读取number的值,2,number的值加,3,写入number的值给number变量。
假如当前nubmer=5
1,线程A读取number的值
2,线程B读取number的值
3,线程B执行加1操作
4,线程B写入最新的number的值。
此时:主内存:number=6,线程B工作内存:number=6,线程A工作内存:number=5。
5,线程A执行加1操作
6,线程A写入最新的number值。
两次number++操作,只增加了1。
解决方案:
1,使用synchronized关键字
2,使用ReentrantLock(java.until.concurrent.locks包下)
3,使用AtomicInterger(java.util.concurrent.atomic包下)
可以看这个文章:
Java并发编程:volatile关键字解析:http://www.importnew.com/18126.html
第一种:
private int number=0;
public int getNumber()
{
return this.number;
}
public synchronized void increase()
{
this.number++;
}
或者:
private int number=0;
public int getNumber()
{
return this.number;
}
public void increase()
{
synchronized(this)
{
this.number++;
}
}
第二种:
对执行加锁处理的地方使用 try finally。。操作,在finally里面,执行lock.unlock释放锁。
private int number=0;
private Lock lock = new ReentrantLock();
public int getNumber()
{
return this.number;
}
public void increase()
{
lock.lock();
try {
this.number++;
}finally {
lock.unlock();
}
}
volatile使用场合
synchronized关键字是防止多个线程同时执行一段代码,那么就会很影响程序执行效率,而volatile关键字在某些情况下性能要优于synchronized,但是要注意volatile关键字是无法替代synchronized关键字的,因为volatile关键字无法保证操作的原子性。通常来说,使用volatile必须具备以下2个条件:
1)对变量的写操作不依赖于当前值
- .不满足:number++,count=count*5等
- .满足:boolean变量,记录温度变化的变量等
2)该变量没有包含在具有其他变量的不变式中
- 不满足:不变式:low<up
.实际上,这些条件表明,可以被写入 volatile 变量的这些有效值独立于任何程序的状态,包括变量的当前状态。
事实上,我的理解就是上面的2个条件需要保证操作是原子性操作,才能保证使用volatile关键字的程序在并发时能够正确执行。
volatile使用场景
(同样摘自文章Java并发编程:volatile关键字解析:http://www.importnew.com/18126.html)
1.状态标记量
volatile boolean flag = false;
while(!flag){
doSomething();
}
public void setFlag() {
flag = true;
}
volatile boolean inited = false;
//线程1:
context = loadContext();
inited = true; //线程2:
while(!inited ){
sleep()
}
doSomethingwithconfig(context);
2.double check
class Singleton{
private volatile static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if(instance==null) {
synchronized (Singleton.class) {
if(instance==null)
instance = new Singleton();
}
}
return instance;
}
}
synchronized和volatile比较
- volatile不需要加锁,比synchronized更轻量级,不会阻塞线程;
- 从内存可见性角度讲,volatile读相当于加锁,volatile写相当于解锁。
- synchronized既能保证可见性,又能保证原子性,而volatile只能保证可见性,无法保证原子性。
Java线程-volatile不能保证原子性的更多相关文章
- Java并发编程之验证volatile不能保证原子性
Java并发编程之验证volatile不能保证原子性 通过系列文章的学习,凯哥已经介绍了volatile的三大特性.1:保证可见性 2:不保证原子性 3:保证顺序.那么怎么来验证可见性呢?本文凯哥(凯 ...
- 为什么volatile不能保证原子性而Atomic可以?
在上篇<非阻塞同步算法与CAS(Compare and Swap)无锁算法>中讲到在Java中long赋值不是原子操作,因为先写32位,再写后32位,分两步操作,而AtomicLong赋值 ...
- volatile之一--volatile不能保证原子性
Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块 和 volatile 关键字机制在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这 ...
- 为什么volatile不能保证原子性?
为什么volatile能替代简单的锁,却不能保证原子性?这里面涉及volatile,是java中的一个我觉得这个词在Java规范中从未被解释清楚的神奇关键词,在Sun的JDK官方文档是这样形容vola ...
- Volatile不保证原子性(二)
Volatile不保证原子性 前言 通过前面对JMM的介绍,我们知道,各个线程对主内存中共享变量的操作都是各个线程各自拷贝到自己的工作内存进行操作后在写回到主内存中的. 这就可能存在一个线程AAA修改 ...
- 【转】为什么volatile不能保证原子性而Atomic可以?
直接上好文链接!!! 为什么volatile不能保证原子性而Atomic可以?
- Java中volatile如何保证long和double的原子性操作
原创转载请注明出处:https://www.cnblogs.com/agilestyle/p/11426473.html 关键字volatile的主要作用是使变量在多个线程间可见,但无法保证原子性,对 ...
- java中volatile不能保证线程安全
今天打了打代码研究了一下java的volatile关键字到底能不能保证线程安全,经过实践,volatile是不能保证线程安全的,它只是保证了数据的可见性,不会再缓存,每个线程都是从主存中读到的数据,而 ...
- java线程--volatile实现可见性
volatile关键字: 1)能够保证volatile变量的可见性 2)不能保证volatile变量复杂操作的原子性. volatile如何实现内存可见性: 深入来说:通过加入内存屏障和禁止重排序优化 ...
随机推荐
- SpringMvc配置详解
在此做一个对Mvc的复习,便于以后快速复习mvc配置. 开发环境 : IDe :iDEA JDK:1.8 使用的框架技术:Mybtais ,Spring,Spring MVC 数据源选用Dbcp 首先 ...
- java23种设计模式之一: 单例模式(Singleton Pattern)
单例模式(Singleton Pattern)是设计模式中比较常用的一种,下面来总结单例模式的知识,包括: 1.理解什么是单例模式.单例模式有什么优点/缺点.单例模式的应用场景: 2.再来看看Java ...
- BZOJ1002輪狀病毒 暴搜 + 找規律 + 高精度
@[暴搜, 找規律, 高精度] Description 轮状病毒有很多变种,所有轮状病毒的变种都是从一个轮状基产生的.一个\(n\)轮状基由圆环上\(n\)个不同的基原子和圆心处一个核原子构成的,2个 ...
- asp.net 将repeater上数据导出到excel
1,首先得到一个DataTable public DataTable GetTable(string sql) { SqlConnnection con=new SqlConnection(Confi ...
- ext:grid分页,列宽度自动填满grid宽度
var cm = new Ext.grid.ColumnModel([{ header : '编号', dataIndex : 'id' }, { header ...
- STM32F10X FLASH and SRAM size
STM32F10X FLASH Size SRAM Size Device Type --------------------------------------------------------- ...
- printf回到上一行开头以及回到本行开头的方法
回到上一行开头 #include <stdio.h> #include <unistd.h> int main(void) { ; ){ printf("%d\n&q ...
- Mac配置PHP开发环境
安装环境如下: Mac OS 10.10.1 Apache 2.4.9 PHP 5.5.14 MySQL 5.6.22 Apache配置 在Mac OS 10.10.1中是自带Apache软件的,我们 ...
- 如何利用Flink实现超大规模用户行为分析
如何利用Flink实现超大规模用户行为分析 各位晚上好,首先感谢大家参与我的这次主题分享,同时也感谢 InfoQ AI 前线组织这次瀚思科技主题月! 瀚思科技成立于 2014 年,按行业划分我们是 ...
- Tomcat与Servlet工作流程
什么是Tomcat? Tomcatserver是一个免费的开放源码的Web 应用server,属于轻量级应用server. 什么是Servlet? Servlet是在server上执行的小程序.,说白 ...