版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_35241080/article/details/82529542
一、volatile
volatile 关键字:当多个线程进行操作共享数据时,可以保证内存中的数据可见。
大家先来看下面一段代码发生情况:
启动一个main主线程,一个子线程,子线程修改共享数据flag可终止main线程的死循环。但是由于子线程修改后main线程无法及时获取新值依然无法终止死循环,对于此情况就是内存可见性导致的问题;
解决方式一:使用volatile修改共享数据flag,那么子线程修改后会立即刷新至主存,主线程使用共享数据每次都会从主存中获取,可获取最新值(推荐);
解决方式二:在循环体中做运算以降低循环速度,使其可以获取最新值;
public class TestVolatile {
public static void main(String[] args) {
ThreadDemo td = new ThreadDemo();
new Thread(td).start();
int i =0;
while(true){//运行结果会导致在这里发生死循环
// System.out.println(i++);方式二
if(td.isFlag()){
//子线程对flag的值修改,但是由于这里运行速度过快导致无法从主存中获取新值;
System.out.println("------------------");
break;
}
}
}
}
class ThreadDemo implements Runnable {
private volatile boolean flag = false;//方式一
@Override
public void run() {
try {
Thread.sleep(200);//导致子线程修改值操作慢于主线程
} catch (InterruptedException e) {
}
flag = true;
System.out.println("flag=" + isFlag());
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
二、volatile的缺点
1.无法保证原子性:一个线程访问时进行读写操作完成后,其他线程才可以访问,volatile不能做到,synchronized、lock可以保证;
2.无法保证互斥性:线程A访问共享数据进行修改的同时无法阻止其他线程操作共享数据
代码解释
class VolatileDemo2 implements Runnable{
private int i=0;
@Override
public void run() {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i++);
//i++ 的原子性问题:i++ 的操作实际上分为三个步骤“读-改-写”
/**
int temp = i; 读
* i = i + 1; 改
* i = temp; 写
*/
}
}
//main
for (int i=0;i<10;i++){
new Thread(demo).start();
}
/**无法保证原子性同时两个线程获取到0;
0 5 8 7 6 4 3 2 1 0
*/
三、解决原子性问题
原子变量:在 java.util.concurrent.atomic 包下提供了一些原子变量。
其底层采用CAS(Compare-And-Swap) 算法保证数据变量的原子性;
CAS 算法是硬件对于并发操作的支持
CAS 包含了三个操作数:
①内存值 V
②预估值 A
③更新值 B
当且仅当 V == A 时, V = B; 否则,不会执行任何操作。
jdk1.8的java.util.concurrent.atomic下关于原子操作的方法
AtomicBoolean
AtomicInteger
AtomicIntegerArray
AtomicIntegerFieldUpdater
AtomicLong
AtomicLongArray
AtomicLongFieldUpdater
AtomicMarkableReference
AtomicReference
AtomicReferenceArray
AtomicReferenceFieldUpdater
AtomicStampedReference
DoubleAccumulator
DoubleAdder
LongAccumulator
LongAdder
Striped64
integer原子类操作
class AtomicDemo implements Runnable{
private AtomicInteger ai = new AtomicInteger(0);
@Override
public void run() {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
}
//获取值后再自增
System.out.println(ai.getAndIncrement());
}
}
//main
AtomicDemo atomicDemo = new AtomicDemo();
for (int i=0;i<10;i++){
new Thread(atomicDemo).start();
}
四、互斥性
class SyncDemo implements Runnable{
private int i = 0;
@Override
public synchronized void run() {
//使用锁,同步代码块同样效果
try {
Thread.sleep(300);
} catch (InterruptedException e) {
}
System.out.print(i++ +",");
//0,1,2,3,4,5,6,7,8,9,
}
}
//main
SyncDemo syncDemo = new SyncDemo();
for (int i=0;i<10;i++){
new Thread(syncDemo).start();
}
synchronized:保证同一时间只能有一个线程访问,相较于volatile是一个重量级的隐式锁
以上代码来源于尚硅谷juc视频:http://www.atguigu.com/