volatile 可以保证可见性和可以保证有序性不保证原子性。
1:可见性: 当我们线程开始时都会把主内存中的共享数据读入到自己的工作线程中,当线程1把值修改到了1,为了保证其他线程可以见会通知其他线程。
2:原子性:当我们线程把共享复制到我们的主线程中的时候,线程1把值修改成1,同时把主内存的共享变量也修改成1,但是在还有修改主内存的同时,线程二抢先一步把主内存的数字3,这样就会导致问题。
3:不保证原子性的解决方案:
(1):使用synchronized,可以使用但是效率比较低,同时synchronized还是重量级的,
(2) : 使用juc 下面的 atumicinteger.CompareAndSet比较并交换 atimicInteger.getAndAddInt
unsafe类这个类在jdk中的在jre/lib下面rt.jar包下面的
用c语言来写的,在unsafe类中有getAndSetInt,中有俩个参数,一个是 当前内存的地址,还有一个是当前对象,底层通过当前对象和地址拿到内存中的值,和自己传入的值作比较,如果当前内存的值,和期望的值是一样的,那么他就会加1,如果不一样那么他就会在那一至自旋,直到内存中的值和期望值是一样的。
vlaueOffset 表示内存偏移地址,unsafe类就可以通过内存偏移地址拿到值
4:volatile如何保证有序性的:单线程的环境下因为不管重排序补充排序都不会对结果照成影响的,所以这里只针对多线程的情况下。我门写的源代码在j计算机中并不是按照我们写的循序去执行的,为什么 volatile怎么解决的呢?
(1):处理器在进行重排序的时候必须考虑指令之间的数据依赖性(这一点必须考虑:比喻:现有你爹才能有你)
(2):多线程环境中由于线程的交替执行,由于编译器优化重排序的存在俩个线程中使用的变量能否保证一致性无法预测。
案例:
多线程的情况下不可能出现4在第一个位置,因为:数据的依耐性
案例2:
最终的结果:可能不会没有打印结果,可能结果是5,可能结果是6
Cas 可以解决原子性的问题但是有问题就是ABA的问题:
(1):如何解决ABA的问题:
利用AtomicReferceDemo
(2):时间戳的原子引用:juc里面有一个AtomicStampReferce
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;
public class ABA {
static AtomicReference<Integer> atomicReference =new AtomicReference<>(100);
static AtomicStampedReference<Integer> integerAtomicStampedReference =new AtomicStampedReference<>(100,1);
public static void main(String[] args) {
// // 线程 t1
// new Thread(() -> {
// atomicReference.compareAndSet(100, 101);
// atomicReference.compareAndSet(101, 100);
// },"t1").start();
// // 线程t2
// new Thread(() -> {
// try{
// TimeUnit.SECONDS.sleep(1); }catch (InterruptedException e){e.printStackTrace();}
// System.out.println(atomicReference.compareAndSet(100,2019)+"\t"+atomicReference.get().toString());
// // true 2019
// },"t2").start();
// 暂停一会线程
try{
TimeUnit.SECONDS.sleep(1); }catch (InterruptedException e){e.printStackTrace();
}
System.out.println("===========================解决ABA问题");
// 线程t4
new Thread(() -> {
int stamp = integerAtomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName()+"\t第一次版本号:"+stamp);
// 暂停一秒
try{ TimeUnit.SECONDS.sleep(1); }catch (InterruptedException e){e.printStackTrace();}
integerAtomicStampedReference.compareAndSet(100,101,integerAtomicStampedReference.getStamp(),integerAtomicStampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName()+"\t第二次版本号:"+integerAtomicStampedReference.getStamp());
integerAtomicStampedReference.compareAndSet(101,100,integerAtomicStampedReference.getStamp(),integerAtomicStampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName()+"\t第三次版本号:"+integerAtomicStampedReference.getStamp());
},"t3").start();
// 线程4
new Thread(() -> {
int stamp = integerAtomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName()+"\t第一次版本号:"+stamp);
// 暂停3秒 t4线程 保证t3 线程完成一次ABA操作
try{ TimeUnit.SECONDS.sleep(3); }catch (InterruptedException e){e.printStackTrace();}
boolean result = integerAtomicStampedReference.compareAndSet(100, 2019, stamp, stamp+1);
System.out.println(Thread.currentThread().getName()+"\t修改成功否:"+result+"\t"+"当前最新版本号:"+integerAtomicStampedReference.getStamp());
System.out.println(Thread.currentThread().getName()+"\t当前实际最新值:"+integerAtomicStampedReference.getReference());
},"t4").start();
}
}