【第75题】JAVA高级技术-多线程9(i++的原子性问题)
回城传送–》《JAVA筑基100例》
零、前言
今天是学习 JAVA语言 打卡的第75天,每天我会提供一篇文章供群成员阅读( 不需要订阅付钱 ),读完文章之后,按解题思路,自己再实现一遍。在小虚竹JAVA社区 中对应的 【打卡贴】打卡,今天的任务就算完成了。
因为大家都在一起学习同一篇文章,所以有什么问题都可以在群里问,群里的小伙伴可以迅速地帮到你,一个人可以走得很快,一群人可以走得很远,有一起学习交流的战友,是多么幸运的事情。
学完后,自己写篇学习报告的博客,可以发布到小虚竹JAVA社区 ,供学弟学妹们参考。
我的学习策略很简单,题海策略+ 费曼学习法。如果能把这100题都认认真真自己实现一遍,那意味着 JAVA语言 已经筑基成功了。后面的进阶学习,可以继续跟着我,一起走向架构师之路。
一、题目描述
题目:重现i++的原子性问题,并解决原子性问题
二、解题思路
并发程序正确地执行,必须要保证原子性、可见性以及有序性。只要有一个没有被保证,就有可能会导致程序运行不正确。
原子性:一个操作或多个操作要么全部执行完成且执行过程不被中断,要么就不执行。
可见性:当多个线程同时访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
有序性:程序执行的顺序按照代码的先后顺序执行。
我们先来说i++的原子性问题
/**
* i++原子性问题:
* eg: int i=10;
* i=i++;//?会等于多少
* i++操作步骤:
* int temp =i;
* i = i +1;
* return temp;
* 所以i++分为三步:“读--》改--》写”
* <p>
* 说到i++,肯定要顺便说下++i,++i的操作步骤:
* int i=10;
* return i=i+1;
**/
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
由上面的例子可知道,i++分为三步:“读–》改–》写”,这个过程就会给并发线程带来风险。
使用Atomic解决原子性问题:
在jdk1.5以后java.util.concurrent.atomic包下,提供了
大量的原子变量,它们内部使用CAS算法。
三、代码详解
重现i++的原子性问题
package com.xiaoxuzhu;
import org.junit.Test;
/**
* Description: 原子性 原子变量
*
* i++原子性问题:
* eg: int i=10;
* i=i++;//?会等于多少
* i++操作步骤:
* int temp =i;
* i = i +1;
* return temp;
* 所以i++分为三步:“读--》改--》写”
* <p>
* 说到i++,肯定要顺便说下++i,++i的操作步骤:
* int i=10;
* return i=i+1;
*
* @author xiaoxuzhu
* @version 1.0
*
* <pre>
* 修改记录:
* 修改后版本 修改人 修改日期 修改内容
* 2022/5/14.1 xiaoxuzhu 2022/5/14 Create
* </pre>
* @date 2022/5/14
*/
public class TestAtomicDemo {
@Test
public void iAtomic() {
int i = 10;
i = i++;
System.out.println("i=" + i);
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
for (int i = 0; i < 10; i++) {
new Thread(myThread).start();
}
}
static class MyThread implements Runnable {
private int serialNumber = 0;
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getSerialNumber());
}
public int getSerialNumber() {
return serialNumber++;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
但不是每次都会出现,可以多试几次
使用Atomic解决原子性问题
package com.xiaoxuzhu;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Test;
/**
* Description:原子性 原子变量 增加Atomic解决原子性问题
*
* i++原子性问题:
* eg: int i=10;
* i=i++;//?会等于多少
* i++操作步骤:
* int temp =i;
* i = i +1;
* return temp;
* 所以i++分为三步:“读--》改--》写”
* <p>
* 说到i++,肯定要顺便说下++i,++i的操作步骤:
* int i=10;
* return i=i+1;
*
* @author xiaoxuzhu
* @version 1.0
*
* <pre>
* 修改记录:
* 修改后版本 修改人 修改日期 修改内容
* 2022/5/14.1 xiaoxuzhu 2022/5/14 Create
* </pre>
* @date 2022/5/14
*/
public class TestAtomicDemo1 {
@Test
public void iAtomic() {
int i = 10;
i = i++;
System.out.println("i=" + i);
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
for (int i = 0; i < 10; i++) {
new Thread(myThread).start();
}
}
static class MyThread implements Runnable {
private AtomicInteger serialNumber = new AtomicInteger();
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getSerialNumber());
}
public int getSerialNumber() {
return serialNumber.getAndIncrement();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
多学一个知识点
CAS算法
CAS(Compare-And-Swap)是一种硬件对并发的支持,针对多处理器操作而设计的,处理器中的一种特殊指令,用于管理对共享数据的并发访问。
CAS是一种无锁的非阻塞算法实现,是硬件对于并发操作的支持,保证了数据变量的原子性。
Cas包含了3个操作数:
- 内存值 V
- 预估值 A
- 更新值 B
当且仅当 V == A 时, V = B; 否则,不会执行任何操作。
简单的来说,CAS有3个操作数,要读写的内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则返回V(不做任何操作,然后重新获取主存V值,重新操作。)。
这是一种 乐观锁 的思路,它相信在它修改之前,没有其它线程去修改它。
package com.xiaoxuzhu;
/**
* Description:CAS算法 简单模拟
*
* 1、内存值 V
* 2、预估值 A
* 3、更新值 B
*
* @author xiaoxuzhu
* @version 1.0
*
* <pre>
* 修改记录:
* 修改后版本 修改人 修改日期 修改内容
* 2022/5/14.1 xiaoxuzhu 2022/5/14 Create
* </pre>
* @date 2022/5/14
*/
public class CASDemo {
public static void main(String[] args) {
final CAS cas = new CAS();
for (int i = 0; i < 20; i++) {
new Thread(new Runnable() {
public void run() {
int expectedValue = cas.getValue();
boolean b = cas.compareAndSet(expectedValue, (int)(Math.random()*100));
System.out.println(b);
}
}).start();
}
}
static class CAS {
//内存值
private volatile int value = 0;
//获取返回内存值 获取值一定要加同步锁
private synchronized int getValue(){
return value;
}
/**
* 如果预估值与原来的值一直,则修改内存为新的值,否则,不做处理。 无论是否修改,都返回原来的内存值。
* 这是用到的是乐观锁
**/
public synchronized int compareAndSwap(int expectedValue, int newValue) {
int oldValue = value;
System.out.println("old:" + oldValue + ",expectedValue:" + expectedValue + ",newValue:" + newValue);
if (expectedValue == oldValue) {
value = newValue;
}
return oldValue;
}
// 如果更新成功,舊的內內存值和預估值相等。
public synchronized boolean compareAndSet(int expectedValue, int newValue) {
return expectedValue == compareAndSwap(expectedValue, newValue);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
四、推荐专栏
五、示例源码下载
关注下面的公众号,回复筑基+题目号
筑基75
文章来源: xiaoxuzhu.blog.csdn.net,作者:小虚竹,版权归原作者所有,如需转载,请联系作者。
原文链接:xiaoxuzhu.blog.csdn.net/article/details/125027219
- 点赞
- 收藏
- 关注作者
评论(0)