五、可见性
image.png
image.png
1、volatile
image.png
image.png
// 请求总数
public static int clientTotal = 5000;
// 同时并发执行的线程数
public static int threadTotal = 200;
public static volatile int count = 0;
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
add();
semaphore.release();
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
log.info("count:{}", count);
}
private static void add() {
count++;
}
结果总是不满5000,证明volatile修饰共享变量不能保证线程安全。原因是volatile不能保证原子性
image.png
六、有序性
image.png
七、安全发布对象
image.png
- 不安全发布对象
public class UnsafePublish {
private String[] states = {"a", "b", "c"};
public String[] getStates() {
return states;
}
public static void main(String[] args) {
UnsafePublish unsafePublish = new UnsafePublish();
log.info("{}", Arrays.toString(unsafePublish.getStates()));
unsafePublish.getStates()[0] = "d";
log.info("{}", Arrays.toString(unsafePublish.getStates()));
}
}
这样发布的对象,是线程不安全的,因为无法假设其它线程会不会像
unsafePublish.getStates()[0] = "d";
一样修改states对象的值,从而造成它状态的错误
- 对象溢出
public class Escape {
private int thisCanBeEscape = 0;
public Escape () {
new InnerClass();
}
private class InnerClass {
public InnerClass() {
log.info("{}", Escape.this.thisCanBeEscape);
}
}
public static void main(String[] args) {
new Escape();
}
}
内部类InnerClass的实例包含了对Escape的封装的引用, 对象没有被正确完全构造完成,就被发布。this 在构造期间溢出。有一个线程还未构造完成就已经看到另一个线程已经构造完成this
image.png
以单例模式为例:/** * 懒汉模式 * 单例实例在第一次使用时进行创建 */ public class SingletonExample1 { // 私有构造函数 private SingletonExample1() { } // 单例对象 private static SingletonExample1 instance = null; // 静态的工厂方法 public static SingletonExample1 getInstance() { if (instance == null) { //线程A,判断instance = null,也进来了。就会初始化两次。 instance = new SingletonExample1(); //线程B进入if里面,但还未new SingletonExample1(); } return instance; } }
/** * 饿汉模式 * 单例实例在类装载时进行创建 */ public class SingletonExample2 { // 私有构造函数 private SingletonExample2() { } // 单例对象 类装载的时候完成初始化,不存在安全问题 private static SingletonExample2 instance = new SingletonExample2(); // 静态的工厂方法 public static SingletonExample2 getInstance() { return instance; } } public class SingletonExample3 { // 私有构造函数 private SingletonExample3() { } // 单例对象 private static SingletonExample3 instance = null; // 静态的工厂方法 synchronized作用在整个方法上,性能不高 public static synchronized SingletonExample3 getInstance() { if (instance == null) { instance = new SingletonExample3(); } return instance; } }
public class SingletonExample4 { // 私有构造函数 private SingletonExample4() { } //正常顺序,分配内存空间->初始化对象->分配对象引用指向 // 1、memory = allocate() 分配对象的内存空间 // 2、ctorInstance() 初始化对象 // 3、instance = memory 设置instance指向刚分配的内存 // JVM和cpu优化,发生了指令重排 // 1、memory = allocate() 分配对象的内存空间 // 3、instance = memory 设置instance指向刚分配的内存 // 2、ctorInstance() 初始化对象 //单线程模式下,是没有问题的。 //多线程模式下,就会有问题。A线程执行到第三步,B线程就认为instance != null ,但实际只是有了对象引用,真实对象还未初始化 // 单例对象 private static SingletonExample4 instance = null; // 静态的工厂方法 public static SingletonExample4 getInstance() { if (instance == null) { // 双重检测机制 // B synchronized (SingletonExample4.class) { // 同步锁 if (instance == null) { instance = new SingletonExample4(); // A - 3 } } } return instance; } }
public class SingletonExample5 { // 私有构造函数 private SingletonExample5() { } // 1、memory = allocate() 分配对象的内存空间 // 2、ctorInstance() 初始化对象 // 3、instance = memory 设置instance指向刚分配的内存 // 单例对象 volatile + 双重检测机制 -> 禁止指令重排 private volatile static SingletonExample5 instance = null; // 静态的工厂方法 public static SingletonExample5 getInstance() { if (instance == null) { // 双重检测机制 // B synchronized (SingletonExample5.class) { // 同步锁 if (instance == null) { instance = new SingletonExample5(); // A - 3 } } } return instance; } }
public class SingletonExample6 { // 私有构造函数 private SingletonExample6() { } // 单例对象 //private static SingletonExample6 instance = new SingletonExample6(); 使用静态域初始化 private static SingletonExample6 instance = null; static { instance = new SingletonExample6(); //还可以使用静态块 } // 静态的工厂方法 public static SingletonExample6 getInstance() { return instance; } public static void main(String[] args) { System.out.println(getInstance().hashCode()); System.out.println(getInstance().hashCode()); } }
public class SingletonExample7 { // 私有构造函数 private SingletonExample7() { } public static SingletonExample7 getInstance() { return Singleton.INSTANCE.getInstance(); } //使用枚举 private enum Singleton { INSTANCE; private SingletonExample7 singleton; // JVM保证这个方法绝对只调用一次 Singleton() { singleton = new SingletonExample7(); } public SingletonExample7 getInstance() { return singleton; } } }