个人笔记--内存可见性和原子变量

jdk1.6以后提供了java并发包。

volatile与内存可见性:

例子:

结果:

结论:

main()线程读取到的td.isFlag并不是true。

这就涉及到了内存可见性问题。

具体原因:

重排序:代码书写的顺序与实际执行的顺序不同。

1.  编译器重排序

2.  指令重排序

3.  内存系统重排序

As-if-serial:

无论如何重排序,程序执行的记过应该与代码顺序执行的结果一致。Java编译器和处理器在运行时都会保证在单线程下遵循这个语言。

补充:jstack可以生成线程快照(jdk/bin)

每一个线程都有单独的内存,只有对一个共享数据进行了操作修改后,才会把更新后的值刷新到主内存中。

但是本例子中,我们使用的是while(true),它的运行速度很快,导致main线程没能读取到线程1修改的值便结束了。于是产生了上面的结果。

这就是内存可见性问题-->当多个线程操作共享数据时,彼此不可见。

解决方法1:

加锁,但是效率太低下了,而且还有其他原因。

这种情况下,我们就可以使用volatile关键字了。

可以把它简单的理解,volatile关键字修饰的变量,就存在主内存中。

原子变量和CAS算法:

结果:

原因:

因为i++有三个步骤 所以这时候不能使用volatile关键字修饰(放在主缓存中 问题还在) 他只能保证内存可见性问题 不能保证原子性问题

解决方法:

也是一种无锁的非阻塞算法的实现。

于是改为下面这种:

原文地址:https://www.cnblogs.com/kz2017/p/8971486.html

时间: 2024-10-07 10:52:01

个人笔记--内存可见性和原子变量的相关文章

java多线程与内存可见性

一.java多线程 JAVA多线程实现的三种方式: http://blog.csdn.net/aboy123/article/details/38307539 二.内存可见性 1.什么是JAVA 内存模型 共享变量 :如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量. Java Memory Model (JAVA 内存模型)描述线程之间如何通过内存(memory)来进行交互,描述了java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存

volatile关键字与内存可见性&原子变量与CAS算法

1 .volatile 关键字:当多个线程进行操作共享数据时, 可以保证内存中的数据可见 2 .原子变量:jdk1.5后java.util.concurrent.atomic 包下提供常用的原子变量 3 .模拟CAS算法 TestVolatile package com.aff.juc; /* 1.volatile 关键字:当多个线程进行操作共享数据时, 可以保证内存中的数据可见 相较于synchronized是一种较为轻量级的同步策略 注意: volatile不具备"互斥性" 不能保

《Java并发编程实战》第十五章 原子变量与非阻塞同步机制 读书笔记

一.锁的劣势 锁定后如果未释放,再次请求锁时会造成阻塞,多线程调度通常遇到阻塞会进行上下文切换,造成更多的开销. 在挂起与恢复线程等过程中存在着很大的开销,并且通常存在着较长时间的中断. 锁可能导致优先级反转,即使较高优先级的线程可以抢先执行,但仍然需要等待锁被释放,从而导致它的优先级会降至低优先级线程的级别. 二.硬件对并发的支持 处理器填写了一些特殊指令,例如:比较并交换.关联加载/条件存储. 1 比较并交换 CAS的含义是:"我认为V的值应该为A,如果是,那么将V的值更新为B,否则不需要修

盲猜原子变量、内存屏障、内存模型、锁之间的关系

1.atomic_flag 和atomic<>的区别,atomic_flag 无论无锁是多大代价(一些cpu可能无锁代价大),都保证atomic_flag 是无锁的.atomic<>会视情况,可能是有锁的也可能是无锁的,哪个开销小选哪个. 2.C++内存模型可以被看作是C++程序和计算机系统(包括编译器,多核CPU等可能对程序进行乱序优化的软硬件)之间的契约,它规定了多个线程访问同一个内存地址时的语义,以及某个线程对内存地址的更新何时能被其它线程看见.C++11 中的 atomic

javascript高级程序设计笔记(第4章 变量、作用域和内存问题)

1. 基本类型值Undefined.Null.Boolean.Number 和 String  基本类型值的复制过程(图解) 基本类型值在内存中占据固定大小的空间,因此被保存在栈内存中;从一个变量向另一个变量复制基本类型的值,会创建这个值的一个副本; 2.引用类型值  变量对象中的变量和保存在堆中的对象(图解) 引用类型的值是对象,保存在堆内存中; 包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向该对象的指针;从一个变量向另一个变量复制引用类型的值,复制的其实是指针,因此两个变量最终

volotile关键字的内存可见性及重排序

在理解volotile关键字的作用之前,先粗略解释下内存可见性与指令重排序. 1. 内存可见性 Java内存模型规定,对于多个线程共享的变量,存储在主内存当中,每个线程都有自己独立的工作内存,并且线程只能访问自己的工作内存,不可以访问其它线程的工作内存.工作内存中保存了主内存中共享变量的副本,线程要操作这些共享变量,只能通过操作工作内存中的副本来实现,操作完毕之后再同步回到主内存当中,其JVM内存模型大致如下图. 而JAVA内存模型规定工作内存与主内存之间的交互协议,其中包括8种原子操作: 1)

原子变量与CAS算法

上一节讨论了 volatile关键字,volatile关键字修饰的作用是不具有 "原子性" 和 "互斥性的" 例如 i++ 操作 就不是一个原子性的操作,i++ 其实分为3个步骤进行 "读-改-写" int temp = i; i = i + 1; i= temp; 先看一段代码: package com.java.juc; public class TestAtomicDemo { public static void main(String[

并发编程之内存可见性

在上篇线程安全中,我们已经知道需要使用锁来同步管理对可变状态的访问操作.今天我们来看下并发编程的内存可见性问题. 同步代码块除了实现原子性或者临界区之外,其还保证了内存可见性,即保证其他线程可以看到状态的变化结果. private static boolean stop =false; private static int number = 0; public static class ReaderThread extends Thread { public void run() { while

J.U.C中的原子变量

在开发过程中,很多时候都需要用到原子的递增递减操作:而我们知道,常用的i ++ 和 i -- 等操作都不是原子的,它包含了三步操作(读-改-写):首先,读取变量i的值,其次将i执行 +1 或者 -1 操作,最后,将计算后的结果赋值给i:通常情况下,只有加锁才能保证 i ++ 和 i -- 等操作的原子性.值得关注的是,在JDK1.5及之后的所有版本,java.concurrent.util.atomic包下给我们提供了原子变量,原子变量支持原子的递增和递减操作.这里,针对原子变量的底层实现原理及