0
点赞
收藏
分享

微信扫一扫

java并发编程(2)--线程 原子性 volatile AtomicInteger

boom莎卡拉卡 2022-03-30 阅读 55


一、原⼦性指的是什么意思

不和分割,完整性,也即某个线程正则做某个具体业务时,中间不可以被加塞或者被分割。

需要 整体完成,要么同时成功,要么同时失败。

package thread;

import java.util.concurrent.TimeUnit;

/**
* volitale关键字是Java提供的⼀种轻量级同步机制。
* 它能够保证可⻅性和有序性
* 但是不能保证原⼦性
* 禁⽌指令重排
*/
class MyData2 {

// int number = 0;
volatile int number = 0;

//此时number前⾯已经加了volatile,但是不保证原⼦性
public void addPlusPlus(){
number++;
}

}

public class VolatileDemo2 {

public static void main(String[] args) {

atomicDemo();

}

/**
* 创建20线程,每个线程执行1000次的number++操作
*/
private static void atomicDemo() {

System.out.println("原⼦性测试");

MyData2 myData = new MyData2();

// 创建20线程
for (int i = 1; i <= 20; i++) {

new Thread(()->{

// 每个线程执行1000次的number++操作
for (int j = 0; j <1000 ; j++) {

// 最终number应该为 number = 20 * 1000 = 20000
myData.addPlusPlus();

}

},String.valueOf(i)).start();

}


// main线程,等待20个线程执行完毕后,打印number变量的值
// 可以发现当前线程组包含:main和Monitor Ctrl-Break
// IntelliJ IDEA执行用户代码的时候,实际是通过反射方式去调用,而与此同时会创建一个Monitor Ctrl-Break 用于监控目的。
while (Thread.activeCount() > 2){

// main线程,执行礼让操作
Thread.yield();

}

System.out.println(Thread.currentThread().getName()+"\t int类型最终 number值: "+myData.number);
}
}

结果:

可⻅,由于 volatile 不能保证原⼦性,出现了线程重复写的问题,最终结果⽐20000⼩。

⽽ AtomicInteger 可以保证原⼦性。

java并发编程(2)--线程 原子性 volatile AtomicInteger_i++

java并发编程(2)--线程 原子性 volatile AtomicInteger_优先级_02


Java线程中的Thread.yield()方法,译为线程让步。

顾名思义,就是说当一个线程使用了这个方法之后,它就会把自己CPU执行的时间让掉,

让自己或者其它的线程运行,注意是让自己或者其他线程运行,并不是单纯的让给其他线程。

 yield()的作用是让步。它能让当前线程由“运行状态”进入到“就绪状态”,从而让其它具有相同优先级的等待线程获取执行权;

但是,并不能保证在当前线程调用yield()之后,​其它具有相同优先级的线程就一定能获得执行权;也有可能是当前线程又进入到“运行状态”继续运行!

      举个例子:一帮朋友在排队上公交车,轮到Yield的时候,他突然说:我不想先上去了,咱们大家来竞赛上公交车。然后所有人就一块冲向公交车,

有可能是其他人先上车了,也有可能是Yield先上车了。

     但是线程是有优先级的,优先级越高的人,就一定能第一个上车吗?这是不一定的,优先级高的人仅仅只是第一个上车的概率大了一点而已,

最终第一个上车的,也有可能是优先级最低的人。并且所谓的优先级执行,是在大量执行次数中才能体现出来的。






二、volatile并不能保证操作的原⼦性

这是因为,⽐如⼀条number++的操作,会形成3条指令。

javap -c 包名.类名


java并发编程(2)--线程 原子性 volatile AtomicInteger_java_03

java并发编程(2)--线程 原子性 volatile AtomicInteger_优先级_04

public void addPlusPlus(); 

Code:
0: aload_0
1: dup
2: getfield #2 // Field number:I //读
5: iconst_1 //++常量1
6: iadd //加操作
7: putfield #2 // Field number:I //写操作
10: return


假设有3个线程,分别执⾏number++,都先从主内存中拿到最开始的值,number=0,然后三个线程分 别进⾏操作。

假设线程0执⾏完毕,number=1,也⽴刻通知到了其它线程,但是此时线程1、2已经拿 到了number=0,所以结果就是写覆盖,线程1、2将number变成1。


解决的⽅式就是:

1. 对 addPlusPlus() ⽅法加锁。

java并发编程(2)--线程 原子性 volatile AtomicInteger_java_05

2. 使⽤ java.util.concurrent.AtomicInteger 类。

java并发编程(2)--线程 原子性 volatile AtomicInteger_i++_06

java并发编程(2)--线程 原子性 volatile AtomicInteger_i++_07

package thread;

import java.util.concurrent.atomic.AtomicInteger;

class MyData3{

//int number=0;
volatile int number=0;

//此时number前⾯已经加了volatile,但是不保证原⼦性
public void addPlusPlus(){
number++;
}

AtomicInteger atomicInteger = new AtomicInteger();
public void addAtomic(){
atomicInteger.getAndIncrement();
}

}

public class VolatileDemo3 {

public static void main(String[] args) {
//volatileVisibilityDemo();
atomicDemo();
}

private static void atomicDemo() {

System.out.println("原⼦性测试");

MyData3 myData=new MyData3();

for (int i = 1; i <= 20; i++) {

new Thread(()->{

for (int j = 0; j <1000 ; j++) {

myData.addPlusPlus();
myData.addAtomic();

}

},String.valueOf(i)).start();

}

while (Thread.activeCount()>2){
Thread.yield();
}

System.out.println(Thread.currentThread().getName()+"\t int类型最终 number值: "+myData.number);
System.out.println(Thread.currentThread().getName()+"\t AtomicInteger类型最终number值: "+myData.atomicInteger);
}
}

结果:

由于 volatile 不能保证原⼦性,出现了线程重复写的问题,最终结果⽐20000⼩。

⽽ AtomicInteger 可以保证原⼦性。

java并发编程(2)--线程 原子性 volatile AtomicInteger_优先级_08
















举报

相关推荐

0 条评论