volatile作用:
-
内存可见性
a. MESI
b. 保证cpu之间缓存一致性
c. volatile 只能可见基本类型,如果是个引用类型,只能可见的是地址值 -
禁止指令重排序
jvm中Object o = new Object() 对象的创建过程
- 在jvm的堆中申请内存(提供默认初始值)
- 给对象赋予成员变量的值
- 把内存地址值给了方法栈中的o
volication的特性
• 数据可见性
• MESI
• 缓存一致性协议
• 阻止cpu命令重排序
• loadfence 源语命令
• storefence 源语命令
• 可以阻止两种命令不能交叉
该代码旨在证明volatile的可见性
• 一个线程的栈内存中有全局变量缓存
• 如果不加volatile,不会及时读取全局变量的值
• 有坑:while循环中 system.out 会刷新内存
private static Boolean isRunning = true;
private static void run() {
while (isRunning) {
try {
Thread.sleep(100);
} catch (Exception e) {
}
System.out.println("程序开始跑啊跑啊跑.....");
}
System.out.println("程序结束了");
}
public static void main(String[] args) {
new Thread(() -> {
run();
}).start();
new Thread(() -> {
try {
Thread.sleep(1000);
} catch (Exception e) {
}
System.out.println("改变成员变量为false");
isRunning = false;
}).start();
}
单例模式
饿汉式
public class HungrySingleton {
private static final HungrySingleton instance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return instance;
}
public static void main(String[] args) {
LazySingleton instance1 = LazySingleton.getInstance();
LazySingleton instance2 = LazySingleton.getInstance();
if(instance1 == instance2){
System.out.println("两个地址值一样");
}
}
}
懒汉式(线程安全的)
public class LazySingleton {
private static volatile LazySingleton instance = null; //保证 instance 在所有线程中同步
private LazySingleton() {
} //private 避免类在外部被实例化
public static LazySingleton getInstance() {
//getInstance 方法前加同步
if (instance == null) {
synchronized (LazySingleton.class) {
if (instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}
public static void main(String[] args) {
}
}
volatile只能保证线程的可见性,但是做不到保证数据一致性所以需要跟synchronized配合
public class Thread_07_volatile_synchronazed {
volatile int count = 0;
synchronized void insertCount(){
try{
Thread.sleep(1);
}catch (Exception e){
}
for (int i = 0; i < 1000; i++) {
count++;
}
}
public static void main(String[] args) {
Thread_07_volatile_synchronazed t = new Thread_07_volatile_synchronazed();
List<Thread> threadList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
threadList.add(new Thread(t::insertCount,"__name"+i));
}
threadList.forEach((o)->o.start());
//挺巧妙的写法,可以等同于 CountDownLatch 但是如果线程报错了,得杀死线程(不建议)
threadList.forEach((o)->{
try{
o.join();
}catch (Exception e){
}
System.out.println(o.getName()+"结束了");
});
System.out.println(t.count);
}
}
AtomicInteger 线程安全的类
public class Thread_08_volatile_atomicInteger {
AtomicInteger count = new AtomicInteger(0);
void insertCount(){
try{
Thread.sleep(1);
}catch (Exception e){
}
for (int i = 0; i < 1000; i++) {
count.incrementAndGet();
}
}
public static void main(String[] args) {
Thread_08_volatile_atomicInteger t = new Thread_08_volatile_atomicInteger();
List<Thread> threadList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
threadList.add(new Thread(t::insertCount,"__name"+i));
}
threadList.forEach((o)->o.start());
//挺巧妙的写法,可以等同于 CountDownLatch 但是如果线程报错了,得杀死线程(不建议)
threadList.forEach((o)->{
try{
o.join();
}catch (Exception e){
}
});
System.out.println(t.count);
}
}
CAS(无锁 乐观锁)
• Compare And Set
• cas(插入值 o,期望值 expected,新值 newValue)
• if o == e
• o = newValue
• else
• try angin or fail
• CAS是CPU级别的指令,不存在多线程情况
ABA问题
• 刚开始是A 处理了一下是B 后来又变回A
• CAS解决不了ABA问题,因为你的插入值没有变
• 如果是基本类型,不影响,如果是引用类型,会有影响
• 解决方案,加版本
• A 版本 1.0
• A ==> B 版本 2.0
• B ==> A 版本 3.0
• CAS的时候加上版本一起比较