博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Volatile-2.不保证原子性
阅读量:4033 次
发布时间:2019-05-24

本文共 3166 字,大约阅读时间需要 10 分钟。

(1)代码理解

变量使用了volatile关键字

package automitic;public class Mydata {
volatile int num=0; public void addto60(){
this.num=60; } public void addplusplus(){
num++; }}

开启20个前程执行1000次num++

package automitic;//验证volatile不保证原子性public class VolatileDemo {
public static void main(String[] args) {
Mydata mydata=new Mydata(); //资源类 //创建20个线程,每个线程操作num++ 1000次 for(int i=0; i<20;i++){
new Thread(()->{
for(int j=0;j<1000;j++) mydata.addplusplus(); },String.valueOf(i)).start(); } //等待20个线程执行结束后,用main线程取值,如果volatile保证原子性,那应该会得到值20000 while (Thread.activeCount()>2){
//这里为什么用2,因为默认后台就会有两个线程,main线程和GC线程 Thread.yield(); } //控制多线程结束后再..的最好方法 System.out.println(Thread.currentThread().getName()+"\t任务完成,num值现在为:"+mydata.num); }}

执行结果

在这里插入图片描述

在这里插入图片描述

发现执行多次的值都不一样,也都不是想当然的20000,当然也有是20000的可能,偶然而已。

所以证明volatile不保证原子性。

(2)原理

volatile不保证原子性,也再次体现了它是一个轻量级,乞丐版

  • 为什么num++的值小于20000

    因为出现了多线程,出现了丢失写值得情况

在这里插入图片描述

比如线程1,2,3都从主物理内存中拿到了num初始值0,在各自得工作内存中,各自自增1, 三个线程都要向主物理内存中写,1线程写入主内存,2,3线程挂起,然后num得值变为1,通知2,3,进程num值已经为1了。但是因为线程非常快,线程2或者3又可能再次把1又写入了主内存,写覆盖,造成了写数据丢失。所以最后得值就很大可能低于20000.恰好等于20000的几率极小。

number++在多线程下是非线程安全的,加synchronized,就能保证是线程安全了

package automitic;public class Mydata {
volatile int num=0; public void addto60(){
this.num=60; } public synchronized void addplusplus(){
num++; }}

加了之后就计算都是20000了

在这里插入图片描述

但是使用synchronized,有点大材小用

(3)不使用synchronized,volatile如何解决原子性

//使用JUP下的AtomicInteger

AtomicInteger atomicInteger= new AtomicInteger();//初始值为0

package Atomic;import java.util.concurrent.atomic.AtomicInteger;public class Mydata {
//基本类型 volatile int num=0; public void addto60(){
this.num=60; } public void addplusplus(){
num++; } //使用带原子性的num++ AtomicInteger atomicInteger= new AtomicInteger();//初始值为0 public void addAtomic(){
atomicInteger.getAndIncrement(); //增1 }}
package Atomic;//验证volatile不保证原子性public class VolatileDemo {
public static void main(String[] args) {
Mydata mydata=new Mydata(); //资源类 //创建20个线程,每个线程操作num++ 1000次 for(int i=0; i<20;i++){
new Thread(()->{
for(int j=0;j<1000;j++) // mydata.addplusplus(); mydata.addmyAtomic(); },String.valueOf(i)).start(); } //等待20个线程执行结束后,用main线程取值,如果volatile保证原子性,那应该会得到值20000 while (Thread.activeCount()>2){
//这里为什么用2,因为默认后台就会有两个线程,main线程和GC线程 Thread.yield(); } //控制多线程结束后再..的最好方法 // System.out.println(Thread.currentThread().getName()+"\t任务完成,int类型 num值现在为:"+mydata.num); System.out.println(Thread.currentThread().getName()+"\t任务完成,AtomicInterger类型 值在为:"+mydata.atomicInteger); }}

运行结果,就一直是20000

在这里插入图片描述

本节新学知识点

1.java后台默认的两个线程 :main, GC

2.主线程main等待其他线程结束后再执行的最佳方法

while (Thread.activeCount()>2){
//这里为什么用2,因为默认后台就会有两个线程,main线程和GC线程 Thread.yield(); //控制多线程结束后再..的最好方法 }
  1. Thread.yield();
    使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。

PS:学习笔记,尚硅谷,周阳大佬

转载地址:http://vvfdi.baihongyu.com/

你可能感兴趣的文章
gdb调试命令的三种调试方式和简单命令介绍
查看>>
C++程序员的几种境界
查看>>
VC++ MFC SQL ADO数据库访问技术使用的基本步骤及方法
查看>>
VUE-Vue.js之$refs,父组件访问、修改子组件中 的数据
查看>>
Vue-子组件改变父级组件的信息
查看>>
Python自动化之pytest常用插件
查看>>
Python自动化之pytest框架使用详解
查看>>
【正则表达式】以个人的理解帮助大家认识正则表达式
查看>>
性能调优之iostat命令详解
查看>>
性能调优之iftop命令详解
查看>>
非关系型数据库(nosql)介绍
查看>>
移动端自动化测试-Windows-Android-Appium环境搭建
查看>>
Xpath使用方法
查看>>
移动端自动化测试-Mac-IOS-Appium环境搭建
查看>>
Selenium之前世今生
查看>>
Selenium-WebDriverApi接口详解
查看>>
Selenium-ActionChains Api接口详解
查看>>
Selenium-Switch与SelectApi接口详解
查看>>
Selenium-Css Selector使用方法
查看>>
Linux常用统计命令之wc
查看>>