14.关于原子性的相关介绍
2. 原子性
2.1 volatile-问题
代码分析 :
package com.itheima.myvolatile;public class Demo {public static void main(String[] args) {MyThread1 t1 = new MyThread1();t1.setName("小路同学");t1.start();MyThread2 t2 = new MyThread2();t2.setName("小皮同学");t2.start();}
}
package com.itheima.myvolatile;public class Money {public static int money = 100000;
}
package com.itheima.myvolatile;public class MyThread1 extends Thread {@Overridepublic void run() {while(Money.money == 100000){}System.out.println("结婚基金已经不是十万了");}
}
package com.itheima.myvolatile;public class MyThread2 extends Thread {@Overridepublic void run() {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}Money.money = 90000;}
}
程序问题 : 女孩虽然知道结婚基金是十万,但是当基金的余额发生变化的时候,女孩无法知道最新的余额。
2.2 volatile解决
以上案例出现的问题 :
当A线程修改了共享数据时,B线程没有及时获取到最新的值,如果还在使用原先的值,就会出现问题
1,堆内存是唯一的,每一个线程都有自己的线程栈。
2 ,每一个线程在使用堆里面变量的时候,都会先拷贝一份到变量的副本中。
3 ,在线程中,每一次使用是从变量的副本中获取的。
Volatile关键字 : 强制线程每次在使用的时候,都会看一下共享区域最新的值
代码实现 : 使用volatile关键字解决
package com.itheima.myvolatile;public class Demo {public static void main(String[] args) {MyThread1 t1 = new MyThread1();t1.setName("小路同学");t1.start();MyThread2 t2 = new MyThread2();t2.setName("小皮同学");t2.start();}
}
package com.itheima.myvolatile;public class Money {public static volatile int money = 100000;
}
package com.itheima.myvolatile;public class MyThread1 extends Thread {@Overridepublic void run() {while(Money.money == 100000){}System.out.println("结婚基金已经不是十万了");}
}
package com.itheima.myvolatile;public class MyThread2 extends Thread {@Overridepublic void run() {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}Money.money = 90000;}
}
2.3 synchronized解决
synchronized解决 :
1 ,线程获得锁
2 ,清空变量副本
3 ,拷贝共享变量最新的值到变量副本中
4 ,执行代码
5 ,将修改后变量副本中的值赋值给共享数据
6 ,释放锁
代码实现 :
package com.itheima.myvolatile2;public class Demo {public static void main(String[] args) {MyThread1 t1 = new MyThread1();t1.setName("小路同学");t1.start();MyThread2 t2 = new MyThread2();t2.setName("小皮同学");t2.start();}
}
package com.itheima.myvolatile2;public class Money {public static Object lock = new Object();public static volatile int money = 100000;
}
package com.itheima.myvolatile2;public class MyThread1 extends Thread {@Overridepublic void run() {while(true){synchronized (Money.lock){if(Money.money != 100000){System.out.println("结婚基金已经不是十万了");break;}}}}
}
package com.itheima.myvolatile2;public class MyThread2 extends Thread {@Overridepublic void run() {synchronized (Money.lock) {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}Money.money = 90000;}}
}
2.4 原子性
概述 : 所谓的原子性是指在一次操作或者多次操作中,要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断,要么所有的操作都不执行,多个操作是一个不可以分割的整体。
代码实现 :
package com.itheima.threadatom;public class AtomDemo {public static void main(String[] args) {MyAtomThread atom = new MyAtomThread();for (int i = 0; i < 100; i++) {new Thread(atom).start();}}
}
class MyAtomThread implements Runnable {private volatile int count = 0; //送冰淇淋的数量@Overridepublic void run() {for (int i = 0; i < 100; i++) {//1,从共享数据中读取数据到本线程栈中.//2,修改本线程栈中变量副本的值//3,会把本线程栈中变量副本的值赋值给共享数据.count++;System.out.println("已经送了" + count + "个冰淇淋");}}
}
代码总结 : count++ 不是一个原子性操作, 他在执行的过程中,有可能被其他线程打断
2.5 volatile关键字不能保证原子性
解决方案 : 我们可以给count++操作添加锁,那么count++操作就是临界区中的代码,临界区中的代码一次只能被一个线程去执行,所以count++就变成了原子操作。
package com.itheima.threadatom2;public class AtomDemo {public static void main(String[] args) {MyAtomThread atom = new MyAtomThread();for (int i = 0; i < 100; i++) {new Thread(atom).start();}}
}
class MyAtomThread implements Runnable {private volatile int count = 0; //送冰淇淋的数量private Object lock = new Object();@Overridepublic void run() {for (int i = 0; i < 100; i++) {//1,从共享数据中读取数据到本线程栈中.//2,修改本线程栈中变量副本的值//3,会把本线程栈中变量副本的值赋值给共享数据.synchronized (lock) {count++;System.out.println("已经送了" + count + "个冰淇淋");}}}
}
2.6 原子性_AtomicInteger
概述:java从JDK1.5开始提供了java.util.concurrent.atomic包(简称Atomic包),这个包中的原子操作类提供了一种用法简单,性能高效,线程安全地更新一个变量的方式。因为变
量的类型有很多种,所以在Atomic包里一共提供了13个类,属于4种类型的原子更新方式,分别是原子更新基本类型、原子更新数组、原子更新引用和原子更新属性(字段)。本次我们只讲解
使用原子的方式更新基本类型,使用原子的方式更新基本类型Atomic包提供了以下3个类:
AtomicBoolean: 原子更新布尔类型
AtomicInteger: 原子更新整型
AtomicLong: 原子更新长整型
以上3个类提供的方法几乎一模一样,所以本节仅以AtomicInteger为例进行讲解,AtomicInteger的常用方法如下:
public AtomicInteger(): 初始化一个默认值为0的原子型Integer
public AtomicInteger(int initialValue): 初始化一个指定值的原子型Integerint get(): 获取值
int getAndIncrement(): 以原子方式将当前值加1,注意,这里返回的是自增前的值。
int incrementAndGet(): 以原子方式将当前值加1,注意,这里返回的是自增后的值。
int addAndGet(int data): 以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果。
int getAndSet(int value): 以原子方式设置为newValue的值,并返回旧值。
代码实现 :
package com.itheima.threadatom3;import java.util.concurrent.atomic.AtomicInteger;public class MyAtomIntergerDemo1 {// public AtomicInteger(): 初始化一个默认值为0的原子型Integer
// public AtomicInteger(int initialValue): 初始化一个指定值的原子型Integerpublic static void main(String[] args) {AtomicInteger ac = new AtomicInteger();System.out.println(ac);AtomicInteger ac2 = new AtomicInteger(10);System.out.println(ac2);}}
package com.itheima.threadatom3;import java.lang.reflect.Field;
import java.util.concurrent.atomic.AtomicInteger;public class MyAtomIntergerDemo2 {// int get(): 获取值
// int getAndIncrement(): 以原子方式将当前值加1,注意,这里返回的是自增前的值。
// int incrementAndGet(): 以原子方式将当前值加1,注意,这里返回的是自增后的值。
// int addAndGet(int data): 以原子方式将参数与对象中的值相加,并返回结果。
// int getAndSet(int value): 以原子方式设置为newValue的值,并返回旧值。public static void main(String[] args) {// AtomicInteger ac1 = new AtomicInteger(10);
// System.out.println(ac1.get());// AtomicInteger ac2 = new AtomicInteger(10);
// int andIncrement = ac2.getAndIncrement();
// System.out.println(andIncrement);
// System.out.println(ac2.get());// AtomicInteger ac3 = new AtomicInteger(10);
// int i = ac3.incrementAndGet();
// System.out.println(i);//自增后的值
// System.out.println(ac3.get());// AtomicInteger ac4 = new AtomicInteger(10);
// int i = ac4.addAndGet(20);
// System.out.println(i);
// System.out.println(ac4.get());AtomicInteger ac5 = new AtomicInteger(100);int andSet = ac5.getAndSet(20);System.out.println(andSet);System.out.println(ac5.get());}
}
2.7 AtomicInteger-内存解析
AtomicInteger原理 : 自旋锁 + CAS 算法
CAS算法:
有3个操作数(内存值V, 旧的预期值A,要修改的值B)
当旧的预期值A == 内存值 此时修改成功,将V改为B
当旧的预期值A!=内存值 此时修改失败,不做任何操作
并重新获取现在的最新值(这个重新获取的动作就是自旋)
2.8 AtomicInteger-源码解析
代码实现 :
package com.itheima.threadatom4;public class AtomDemo {public static void main(String[] args) {MyAtomThread atom = new MyAtomThread();for (int i = 0; i < 100; i++) {new Thread(atom).start();}}
}
package com.itheima.threadatom4;import java.util.concurrent.atomic.AtomicInteger;public class MyAtomThread implements Runnable {//private volatile int count = 0; //送冰淇淋的数量//private Object lock = new Object();AtomicInteger ac = new AtomicInteger(0);@Overridepublic void run() {for (int i = 0; i < 100; i++) {//1,从共享数据中读取数据到本线程栈中.//2,修改本线程栈中变量副本的值//3,会把本线程栈中变量副本的值赋值给共享数据.//synchronized (lock) {// count++;
// ac++;int count = ac.incrementAndGet();System.out.println("已经送了" + count + "个冰淇淋");// }}}
}
源码解析 :
//先自增,然后获取自增后的结果
public final int incrementAndGet() {//+ 1 自增后的结果//this 就表示当前的atomicInteger(值)//1 自增一次return U.getAndAddInt(this, VALUE, 1) + 1;
}public final int getAndAddInt(Object o, long offset, int delta) {//v 旧值int v;//自旋的过程do {//不断的获取旧值v = getIntVolatile(o, offset);//如果这个方法的返回值为false,那么继续自旋//如果这个方法的返回值为true,那么自旋结束//o 表示的就是内存值//v 旧值//v + delta 修改后的值} while (!weakCompareAndSetInt(o, offset, v, v + delta));//作用:比较内存中的值,旧值是否相等,如果相等就把修改后的值写到内存中,返回true。表示修改成功。// 如果不相等,无法把修改后的值写到内存中,返回false。表示修改失败。//如果修改失败,那么继续自旋。return v;
}
2.9 悲观锁和乐观锁
synchronized和CAS的区别 :
**相同点:**在多线程情况下,都可以保证共享数据的安全性。
**不同点:**synchronized总是从最坏的角度出发,认为每次获取数据的时候,别人都有可能修改。所以在每 次操作共享数据之前,都会上锁。(悲观锁)
cas是从乐观的角度出发,假设每次获取数据别人都不会修改,所以不会上锁。只不过在修改共享数据的时候,会检查一下,别人有没有修改过这个数据。
如果别人修改过,那么我再次获取现在最新的值。
如果别人没有修改过,那么我现在直接修改共享数据的值.(乐观锁)
14.关于原子性的相关介绍相关推荐
- Android O 前期预研之二:HIDL相关介绍
在上一篇博客里,大致介绍了下Android O 中treble计划的一些背景与相关基本架构,这一篇中跟大家一起来探讨下HIDL相关的内容. Android HAL类型 在此之前的ANDROID版本当 ...
- 阿里MNN推理框架相关介绍
一.参考资料 MNN官网 中文文档-语雀 欢迎使用MNN文档 - MNN-Doc 2.1.1 documentation) 英文文档 MNN知识库 MNN 官方仓库 二.相关介绍 1. MNN简介 M ...
- intra-mart产品开发相关介绍资料
intra-mart产品开发相关介绍资料 产品介绍 iap安装手册 script开发手册 tgfw开发手册 im_workflow开发手册 im_workflow_tgfw开发手册 im_workfl ...
- 基于FPGA的SRIO的相关介绍和实现
SRIO的相关介绍和实现 1.SRIO简介 SRIO是面向嵌入式系统开发提出的高可靠.高性能.基于包交换的新一代高速互联技术,已于2004年被国际标准化组织(ISO)和国际电工协会(IEC)批准为IS ...
- DICOM3.0中的VR相关介绍
最近在跟一个关于医疗的项目,所以了解了一下DICOM3.0协议. DICOM(Digital Imaging and Communications in Medicine)即医学数字成像和通信,是医学 ...
- 一文整理14道MySQL索引相关面试题
精心整理14道MySQL索引相关面试题(珍藏版) 如果仅仅是死记硬背MySQL索引相关面试题一定是相当枯燥的,不容易记却容易忘,这里循序渐进的讲解有关索引有关知识点,让大家在理解的基础上记住一些面试常 ...
- AI芯片-算力相关介绍
一.参考资料 TOPS(处理器运算能力单位) 芯片的算力到底有什么用?算力是怎么评估的? NPU架构与算力分析 二.相关介绍 1. FLOPS FLOPS(floating-point operati ...
- pthread相关介绍
pthread相关介绍 int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void*(*start_routine)(voi ...
- axios队列 vue_(十三 )Vue 封装axios(四种请求)及相关介绍
Vue 封装axios(四种请求)及相关介绍 首先axios是基于promise的http库 promise是什么? 1.主要用于异步计算 2.可以将异步操作队列化,按照期望的顺序执行,返回符合预期的 ...
最新文章
- js中 let var const 的差异和使用场景
- 码云创建maven工程
- 有理贝塞尔曲线(Rational Bezier Curves)
- 【杂谈】如何使用有三AI生态学习计算机视觉和自然语言处理等内容
- 操作系统(六)系统调用
- VTK:标记网格用法实战
- apt-get install用发
- python旋转排序数组_LeetCode(力扣)——Search in Rotated Sorted Array 搜索旋转排序数组 python实现...
- Team Foundation Server (TFS) 2015 安装指导
- 在mysql中存储生日,php中计算今天是否为用户生日
- redis命令_Redis 命令执行过程(上)
- Nifi从入门到精通(一)之 数据存储
- mysql主从配置修改重启无效
- 深度学习之图像隐写去除(DDSP模型 Steganography Removal)
- 对于Mixin(混入)详情介绍
- Java反射--获取类方法的泛型返回或泛型参数
- Itext生成图表类型PDF
- falagard cegui
- java 中文域名解析idn_中文域名 原理剖析
- 求 有关数字功放的设计报告 见详情
热门文章
- docker nginx 配置负载均衡
- Survey | 多任务学习综述
- 从大规模文献库中实现生物医学关系抽取
- ld-linux-x86-64.so.2+,RedHat6安装Oracle数据库遇到错误 C [ld-linux-x86-64.so.2+0x14d70]
- mSystems:生物地球化学进入病毒时代-采用多样的方法研究病毒和生物地球化学循环...
- Nature子刊:遗传发育所白洋组发表高通量分离培养和鉴定根系细菌的方法
- R语言可视化学习笔记之ggridges包
- NBT | 利用血液代谢组预测肠道微生物组的多样性
- 易生信-扩增子教程02-真菌引物选择
- R语言把dataframe数据转化为tibble格式、查看每个数据列的缺失值个数、使用数据列的均值对数据列的缺失值进行填充