多线程(四)~数据操作的原子性,使用原子性操作AutomicInteger替换非原子性的i++的操作
这一章,我们要来验证volatile关键字不是原子性的,OK,还是用代码来说话。
①.线程类,操作i++ 500次
package com.multiThread.thread;
publicclassNumberThreadimplementsRunnable{
privatevolatileint num =0;
@Override
publicvoid run(){
for(int i =0;i<500;i++){
num++;
}
}
publicint getNum(){
return num;
}
publicvoid setNum(int num){
this.num = num;
}
}
②.测试类,创建5个线程同时执行
package com.multiThread.test.automic;
import com.multiThread.thread.NumberThread;
publicclassAutomicTest{
publicstaticvoid main(String[] args){
NumberThread numThread =newNumberThread();
for(int i =0;i<5;i++){
Thread t =newThread(numThread);
t.start();
}
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(numThread.getNum());
}
}
多次运行结果:(正常结果2500居多)
2500
2282
2458
为什么造成这种现象?
首先我们要知道i++不是原子操作,是线程不安全的,它分为以下3步:
1.获取i的值
2.执行i+1的操作
3.将结果赋值给i
其次就算变量已经使用volatile关键字来修饰,只能保证读取全局变量num值的时候从主存拿到的是最新的值。
但是当多个线程同时操作自加的时候,如果之前取到的是同一个值,再自加后得到的值是相同的。
解决的方式如下两种:
1.可以使用synchronize关键字进行同步操作,这种情况需要把volatile去掉。因为synchronize本身就会操作工作内存和主内存直接的数据同步,此方式不再赘述。
2.将i++更改为线程安全的原子性操作,使用AtomicInteger替代i++
①.线程类
package com.multiThread.thread;
import java.util.concurrent.atomic.AtomicInteger;
publicclassNumberThread2implementsRunnable{
privateAtomicInteger num =newAtomicInteger();
@Override
publicvoid run(){
for(int i =0;i<500;i++){
num.incrementAndGet();
}
}
publicAtomicInteger getNum(){
return num;
}
publicvoid setNum(AtomicInteger num){
this.num = num;
}
}
②.测试类
package com.multiThread.test.automic;
import com.multiThread.thread.NumberThread;
import com.multiThread.thread.NumberThread2;
publicclassAutomicTest{
publicstaticvoid main(String[] args){
NumberThread2 numThread2 =newNumberThread2();
for(int i =0;i<5;i++){
Thread t =newThread(numThread2);
t.start();
}
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(numThread2.getNum());
}
}
运行结果
2500