マルチスレッド環境での1、シングルトン潜在的なセキュリティ問題
package com.example.mybaties;
/**
* @DESCRIPTION 单例模式在多线程环境下可能存在安全问题
* @Author lst
* @Date 2020-03-24 09:00
*/
public class SingletonDemo {
//高并发下加载完成再取值 volatile(禁止指令重排),不加volatile,99.9%是成功的
private static volatile SingletonDemo instance = null;
public SingletonDemo(){
System.out.println(Thread.currentThread().getName()+"\t 我是构造方法SingletonDemo() ");
}
/**
* 可以加synchronized(效率低,太重了)
* @return
*/
/* public static synchronized SingletonDemo getInstance(){
if(instance == null){
instance = new SingletonDemo();
}
return instance;
}*/
/**
* 高并发下 DCL双端检索机制
*/
public static synchronized SingletonDemo getInstance(){
if(instance == null){
//同步代码块 加锁前后判断
synchronized (SingletonDemo.class){
if(instance == null){
instance = new SingletonDemo();
}
}
}
return instance;
}
public static void main(String[] args) {
//单线程
/* System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());*/
/**
* main 我是构造方法SingletonDemo()
* true
* true
* true
*/
//并发多线程后,情况发生了很大的变化
for (int i = 1; i <= 10; i++) {
new Thread(() ->{
SingletonDemo.getInstance();
},String.valueOf(i)).start();
}
/**
* 1 我是构造方法SingletonDemo()
* 5 我是构造方法SingletonDemo()
* 3 我是构造方法SingletonDemo()
* 2 我是构造方法SingletonDemo()
*/
}
}
2、CASの実装原理
比較およびスワップ(Conmpareとスワップ)マルチスレッドの同期を実現するためのアトミック命令です。JAVA1.5がCASの導入を開始し、メインコードは、原子JUCパッケージの下に配置されています
CAS(楽観的ロック)を効果的に並行処理の効率を向上させるだけでなく、ABAの問題を導入することができます。
メモリ・X、A、別のスレッドA 2は、メモリXから除去され、このとき、スレッド2から除去スレッド1がターン・スレッド2内のメモリにB、およびXのメモリXに値の一部を操作しますデータとなり、スレッド1 CAS操作がまだメモリXで発見され、この時間は、1つの操作が成功したスレッドです。CASの成功は、1つの操作をスレッドが、全体のプロセスには問題があるが。例えば、リストの先頭の変化は、二人は元の値に戻った後、しかし、リストは変更はありませんという意味ではありません。
JAVAは、問題が発生して、対処する主オブジェクトにターゲットを変更したかどうかを識別するために、追加のタグを追加するAtomicStampedReference / AtomicMarkableReferenceのABAのシーンを提供しますので。
package com.example.mybaties;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @DESCRIPTION CAS
* 1、cas是什么?compareAndSet(期望值,修改值)
* 比较并交换
* unsafe和自旋锁
* ①、unsafe是CAS的核心类,由于Java方法无法直接访问底层系统,需要通过本地( native)方法来访问, Unsafe相当于一个后门,基于该类可以直接操作特定内存的数据,
* Unsafe类存在于sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存,因为Java中CAS操作的执行依赖于 Unsafe类的方法。
* 注意 Unsafe类中的所自方法都是 native修饰的,也就是说 Unsafe类中的方法都直接调用操作系统底层资源执行相应任务
* ②、自旋锁代码原理
* 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;
* }
* CAS缺点:① 如果CAS失敗,会一直进行尝试。如果CAS长时间一直不成功,可能会给CPU带来很大的开销
* ② 只能保证一个共享变量的原子操作
* ③ 引出ABA问题(狸猫换太子)
* CAS--->Unsafe ---》cas底层思想 ---》ABA ---》原子引用更新 ---》如何规避ABA问题
* @Author lst
* @Date 2020-03-24 09:00
*/
public class CasDemo {
public static void main(String[] args) {
//主物理内存是5
AtomicInteger atomicInteger = new AtomicInteger(5);
//main do thing
System.out.println(atomicInteger.compareAndSet(5,2020) + "\t current data:"+ atomicInteger.get());
/**
* true current data:2020
*/
System.out.println(atomicInteger.compareAndSet(5,1024) + "\t current data:"+ atomicInteger.get());
/**
* true current data:2020
* false current data:2020
*/
//atomicInteger.getAndIncrement();
}
}
ABAの問題を解決するために3、
package com.example.mybaties;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;
/**
* @DESCRIPTION ABA问题的解决
* @Author lst
* @Date 2020-03-26 09:00
*/
public class AbaDemo { //AtomicStampedReference
static AtomicReference atomicReference = new AtomicReference(100);
static AtomicStampedReference atomicStampedReference = new AtomicStampedReference(100,1);
public static void main(String[] args) {
new Thread(()->{
atomicReference.compareAndSet(100,101);
atomicReference.compareAndSet(101,100);
},"t1").start();
new Thread(()->{
//暂停一会线程
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicReference.compareAndSet(100,2020) + "\t "+ atomicReference.get());
/**
* true 2020
*/
},"t2").start();
//暂停一会线程
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("====================以下是ABA问题的解决==================");
new Thread(()->{
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + "\t 第一次版本:" + stamp);
//暂停一会线程
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName() + "\t 第二次版本:" + atomicStampedReference.getStamp());
atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName() + "\t 第三次版本:" + atomicStampedReference.getStamp());
},"t3").start();
new Thread(()->{
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + "\t 第一次版本:" + stamp);
//暂停一会线程 保证上面的t3线程完成ABA操作
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean result = atomicStampedReference.compareAndSet(100,2020,stamp,stamp+1);
System.out.println(Thread.currentThread().getName() + "\t 修改成功否" + result + "\t 当前最新实际版本号:" + atomicStampedReference.getStamp());
System.out.println(Thread.currentThread().getName() + "\t 当前实际最新值:" + atomicStampedReference.getReference());
},"t4").start();
/**
* true 2020
* ====================以下是ABA问题的解决==================
* t3 第一次版本:1
* t4 第一次版本:1
* t3 第二次版本:2
* t3 第三次版本:3
* t4 修改成功否false 当前最新实际版本号:3
* t4 当前实际最新值:100
*/
}
}