JUC之JMM和volatile

1 JMM

1.1 计算机硬件存储体系

在这里插入图片描述

1.2 为什么需要JMM

在这里插入图片描述

1.3 JMM定义

在这里插入图片描述

1.4 JMM特性

1.4.1 可见性

在这里插入图片描述
在这里插入图片描述

1.4.2 原子性

1.4.3 有序性

1.5 happens-before

1.5.1 8条规则

(1)次序规则:一个线程内,按照代码顺序,写在前面的操作先行发生于写在后面的操作 。
(2)锁定规则:一个unlock操作先行发生于后面对同一个锁的lock操作。
(3)volatile变量规则:对一个volatile变量的写操作先行发生于后面(时间上的先后)对这个变量的读操作。
(4)传递规则:如果操作A先行发生于B,B先行发生于C,那么A先行发生于C。
(5)线程启动规则:Tread对象的start方法先行发生于此线程的每一个动作。
(6)线程中断规则:
在这里插入图片描述
(7)线程终止规则:
在这里插入图片描述
(8)对象终结规则:
在这里插入图片描述
在这里插入图片描述

2 volatile

2.0 特点

可见性。
有序性。
不保证原子性。

2.1 内存语义

(1)写变量时,会把线程中的变量副本刷回主存。
(2)通知其他线程,副本作废,重新回主存中去重新读取变量
(3)总结:就是写的时候,写到内存中。读的时候直接从内存中读取。

2.2 内存屏障

2.2.1 什么是内存屏障

在这里插入图片描述
在这里插入图片描述

2.2.2 分类

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2.2 volatile变量规则

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2.3 可见性case解析

2.2.3.1 案例
package org.example.volatiletest;

import java.util.concurrent.TimeUnit;

/**
 * 如果flag没有用volatile修饰,线程一一直在执行,出不来的。
 * 用了的话,就可以出来。
 */
public class Test {
    private static volatile boolean flag = true;

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                String name = Thread.currentThread().getName();
                System.out.println(String.format("----------- thread %s come in--------", name));
                while (flag) {

                }
                System.out.println(String.format("----------- thread %s come out--------", name));
            }
        }).start();

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        flag = false;
        System.out.println(String.format("----------- main thread will come out, flag = %s--------", flag));
    }
}

2.2.3.2 解析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2.4 原子性case解析

2.2.4.1 案例
package org.example.volatiletest;

import java.util.concurrent.TimeUnit;

/**
 * synchronized修饰会得到稳定的10000的结果。
 * volatile就保证不了
 */
class MyNumber {
    private volatile int number;

    public void addPlusPlus() {
        number++;
    }

    public int getNumber() {
        return number;
    }
}

public class AtomicTest {
    public static void main(String[] args) {
        MyNumber myNumber = new MyNumber();

        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < 1000; j++) {
                        myNumber.addPlusPlus();
                    }
                }
            }).start();
        }

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(myNumber.getNumber());
    }
}
2.2.4.2 解析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2.5 有序性case解析

在这里插入图片描述

2.2.5.1 案例

在这里插入图片描述

2.2.5.2 解析

2.2.6 volatile的使用场景

(1)单一赋值可以,但是包含复合操作的不可以(i++等操作)。
(2)状态标志, boolean变量的读写。
(3)开销较低的读写锁策略。
在这里插入图片描述
(4)两重校验锁的单例模式
在这里插入图片描述
在这里插入图片描述
(5)与AtomicIntegerFieldUpdater等配合使用,实现对对象属性的原子性的修改。

线程范围内共享数据

package com.sk.thread;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class ThreadScopeShareData {

	private static int data = 0;
	private static Map<Thread, Integer> threadData = new HashMap<Thread, Integer>();
	public static void main(String[] args) {
		for(int i=0;i<2;i++){
			new Thread(new Runnable(){
				@Override
				public void run() {
					int data = new Random().nextInt();
					System.out.println(Thread.currentThread().getName() 
							+ " has put data :" + data);
					threadData.put(Thread.currentThread(), data);
					new A().get();
					new B().get();
				}
			}).start();
		}
	}
	
	static class A{
		public void get(){
			int data = threadData.get(Thread.currentThread());
			System.out.println("A from " + Thread.currentThread().getName() 
					+ " get data :" + data);
		}
	}
	
	static class B{
		public void get(){
			int data = threadData.get(Thread.currentThread());			
			System.out.println("B from " + Thread.currentThread().getName() 
					+ " get data :" + data);
		}		
	}
}

有时候会抛异常,没搞懂

Thread-0 has put data :-1772161753
Thread-1 has put data :-566086542
A from Thread-0 get data :-1772161753
Exception in thread "Thread-1" B from Thread-0 get data :-1772161753
java.lang.NullPointerException
	at com.sk.thread.ThreadScopeShareData$A.get(ThreadScopeShareData.java:29)
	at com.sk.thread.ThreadScopeShareData$1.run(ThreadScopeShareData.java:20)
	at java.lang.Thread.run(Thread.java:745)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值