volitile
volatile保证线程的可见性,同时防止指令的重排序,
线程可见性在CPU级别使用缓存一致性来保证的,
禁止指令重排序是CPU级别,是你禁止不了的,是内部运行的过程,提高效率的,
但是在加了volatile之后,这个指令重排序就可以禁止了,
严格来说,他是加了读屏障和写屏障,是CPU的一个源语。
1.保证线程可见性:
如果没有volatile修饰,那么线程不会终止.
每个线程都是在自己的运行空间执行操作,何时将flag写会公共区域不可知,加上volatila修饰flag,保证可见性
public class T1 {
volatile boolean run = true;
public void m1(){
System.out.println("m1 start ...");
while (run){
//System.out.println("m1 runing ...");
}
System.out.println("m1 stop ...");
}
public static void main(String[] args) throws InterruptedException {
T1 t = new T1();
new Thread(t::m1,"t1").start();
Thread.sleep(1000);
t.run = false;
}
}
输出:
m1 start ...
m1 stop ...
2.禁止指令重排序:
指令重排序:和CPU有关系,CPU会并行执行多条执行,加了volatile,编译的时候,将不会进行重排序
- DCL单例
- 饿汉式
//饿汉式 先初始化好对象,调用instance方法的时候进行返回
public class Mgr01 {
private static final Mgr01 m = new Mgr01();//私有方法产生对象
private Mgr01(){}//私有构造方法禁止其他类new对象
public static Mgr01 getInstance(){
return m;//共有方法产生对象
}
public static void main(String[] args) {
Mgr01 m1 = Mgr01.getInstance();
Mgr01 m2 = Mgr01.getInstance();
System.out.println(m1 == m1);
}
}
-
懒汉式
多线程不安全
public class Mgr02 {
private static Mgr02 mgr02;
private Mgr02(){}
//多线程调用会产生多个对象
public static Mgr02 getInstance(){
if(mgr02 == null){
System.out.println("mgr02 is null");
mgr02 = new Mgr02();
}
return mgr02;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->
System.out.println(Mgr02.getInstance().hashCode())
).start();
}
}
}
输出:
mgr02 is null
mgr02 is null
1117110646
mgr02 is null
122651855
mgr02 is null
1343599390
mgr02 is null
588558273
mgr02 is null
1926683415
588558273
588558273
1343599390
- 懒汉式 双重检查
public class Mgr03 {
private static /*volatile*/ Mgr03 INSTANCE;
private Mgr03(){}
public static Mgr03 getInstance(){
if(INSTANCE == null){
System.out.println(Thread.currentThread().getName()+" is null");
synchronized (Mgr03.class){
System.out.println(Thread.currentThread().getName()+" get lock!");
if(INSTANCE == null){
System.out.println(Thread.currentThread().getName()+" get Object!");
INSTANCE = new Mgr03();
}
}
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->
System.out.println(Mgr03.getInstance().hashCode())
,"Thread :"+ i).start();
}
}
}
输出:
Thread :0 is null
Thread :5 is null
Thread :6 is null
Thread :7 is null
Thread :0 get lock!
Thread :3 is null
Thread :4 is null
Thread :0 get Object!
1926683415
Thread :8 is null
1926683415
Thread :9 get lock!
1926683415
-
懒汉式 双重检查,要不要加volatile?
- 要加volatile,原因是在指令重排序上,JVM创建对象有三部:申请内存,初始化,赋值对象
- 指令重排序有时候会将赋值和初始化调换位置
- 在高并发情况下,会拿到还没有被赋值的对象,而不是赋值之后的对象
- 加了volatile之后,不会发生指令重排序,保证多线程获取到对象一定是赋值之后的对象
对象赋值三部:
- volatile 只能保证可见性,不能保证synchronized原子性
- Doulbe Check Lock
- 读写屏障
###CAS(无锁优化 、乐观锁)
Atomic开头的都是CAS操作
1.Atomic
public class Atomic01 {
AtomicInteger atomicInteger = new AtomicInteger(0);
void m1(){
for (int i = 0; i < 10000; i++) {
atomicInteger.incrementAndGet();//自增
}
}
public static void main(String[] args) {
Atomic01 atomic01 = new Atomic01();
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < 10; i++) {
threads.add(new Thread(atomic01::m1,"thread - " +i));
}
threads.forEach((o) ->o.start());//线程启动
threads.forEach((o) -> {
try {
o.join();//线程顺序执行
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(atomic01.atomicInteger);
}
}
输出:
Connected to the target VM, address: '127.0.0.1:52118', transport: 'socket'
100000
Disconnected from the target VM, address: '127.0.0.1:52118', transport: 'socket'
原理
- CAS(当前值V,期望值E,新值N)
如果V=E,则设置N,否则设置失败,重新来一次
这是CPU原语,是不能被打算的
//unsafe类
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
//
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
//
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
ABA问题
使用CAS会产生问题:
原值是A,改成了B,又改成了A
如果是基础类型:无所谓,不影响结果
如果是引用类型:中间经历了别人
解决方法:加version版本,每次做CAS的时候,检查版本号
Unsafe类
- 单例
//私有构造器
private Unsafe() {
}
//获取unsafe对象
@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
所有的CAS底层都是调用Unsafe类的compareAndSwap方法,直接操作操作系统