L1 Cache,分为数据缓存和指令缓存,逻辑核独占
L2 Cache,物理核独占,逻辑核共享
L3 Cache,所有物理核共享
以下代码证明cpu的性能验证
public class testUntil {
private static final int RUNS = 100;
private static final int DIMENSION_1 = 1024 * 1024;
private static final int DIMENSION_2 = 6;
private static long[][] longs;
public static void main(String[] args) throws Exception {
/*初始化数组*/
longs = new long[DIMENSION_1][];
for (int i = 0; i < DIMENSION_1; i++) {
longs[i] = new long[DIMENSION_2];
for (int j = 0; j < DIMENSION_2; j++) {
longs[i][j] = 1L;
}
}
System.out.println("Array初始化完毕");
long sum = 0L;
long start = System.currentTimeMillis();
for (int i = 0; i < DIMENSION_1; i++) {
for (int j = 0; j < DIMENSION_2; j++) {//6
sum += longs[i][j];
}
}
System.out.println("spend time1:" + (System.currentTimeMillis() - start));
System.out.println("sum1:" + sum);
sum = 0L;
start = System.currentTimeMillis();
for (int j = 0; j < DIMENSION_2; j++) {//6
for (int i = 0; i < DIMENSION_1; i++) {//1024*1024
sum += longs[i][j];
}
}
System.out.println("spend time2:" + (System.currentTimeMillis() - start));
System.out.println("sum2:" + sum);
}
}
结果
Array初始化完毕
spend time1:14 //cpu是一组数据横向读取的
sum1:6291456
spend time2:48
sum2:6291456
一下代码证明了volatile的可见性
public class test {
public volatile static int i = 1;
public static void increase() {
i++;
System.out.println("输出i++:" + i);
//逻辑核的核数
Runtime.getRuntime().availableProcessors();
}
volatile boolean initFlag = false;
public void save() {
this.initFlag = true;
final String name = Thread.currentThread().getName();
System.out.println("线程:" + name + "修改共享变量initFlag");
}
public void load() {
final String name = Thread.currentThread().getName();
while (!initFlag) {
//线程此处跑空,等待initFlag修改了状态
System.out.println("此处跑空线程");
}
System.out.println("线程:" + name + "当前线程嗅到了initFlag改变状态");
}
public static void main(String[] args) {
test test = new test();
Thread threadA = new Thread(() -> {
test.save();
increase();
}, "threadA");
Thread threadB = new Thread(() -> {
test.load();
increase();
}, "threadB");
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadA.start();
}
}
结果
此处跑空线程
此处跑空线程
此处跑空线程
线程:threadB当前线程嗅到了initFlag改变状态
线程:threadA修改共享变量initFlag
输出i++:3
输出i++:2
//结论,不加volatile 也可以得到上面的结果,但是volatile 可以让其他线程实时更新变量
以下代码证明了volatile不能保证线程的原子性
public class Jmm04 {
private volatile static int counter = 0;
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
counter++;
}
});
thread.start();//新建10个线程并开启
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("counter:" + counter);
}
}
运行结果
counter:9535(并没有得到一万)
以下代码证明了volatile可以防止指令重排
public class Jmm05 {
private volatile static int x = 0,y = 0;//如果不加volatile ,就是用UnsafeInstance
private volatile static int a = 0, b= 0;
public static void main(String[] args) throws InterruptedException{
int i = 0;
for (;;){
i++;
x = 0;y = 0;
a = 0; b= 0;
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
shortWait(10000);
a = 1;
//如果不加volatile,怎么进行手动加内存屏障,防止指令重排呢?
//有volatile则不需要以下这行代码
UnsafeInstance.reflectGetUnsafe().fullFence();
x = b;
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
b = 1;
//有volatile则不需要以下这行代码
UnsafeInstance.reflectGetUnsafe().fullFence();
y = a;
}
});
thread1.start();
thread2.start();
thread1.join();//等待线程1运行完再继续运行
thread2.join();
String result = "第"+i+"次("+x+","+y+")";
if (x==0 && y==0){
System.out.println("打印输出:"+result);
break;
}else {
System.out.println("log:"+result);
}
}
}
public static void shortWait(long interval){
long start = System.nanoTime();
long end;
do {
end = System.nanoTime();
}while (start+interval>=end);
}
}
UnsafeInstance是1.7版本以后的防止指令重排的工具类
/**
*
* 自动加屏障
*
*/
public class UnsafeInstance {
public static Unsafe reflectGetUnsafe() {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
return (Unsafe) theUnsafe.get(null);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
以下代码证明,指令重拍的严重性
public class DoubleCheckLock {
private volatile static DoubleCheckLock instance;//懒汉模式
public static DoubleCheckLock getInstance() {
if (instance == null) {
//同步对象
synchronized (DoubleCheckLock.class) {
//禁止重复创建
if (instance == null) {
//确定创建
instance = new DoubleCheckLock();
}
}
}
return instance;
}
}
如果指令重排了,其他线程就会出错,抛出异常
1memory = allocate();//1.分配对象内存空间
2instance(memory);//2.初始化对象
3 instance = memory;//3.设置instance指向刚分配的内存地址,此时instanc e!=null
如果指令重排
1memory=allocate();//1.分配对象内存空间
2instance=memory;//3.设置instance指向刚分配的内存地址,此时instanc e!=null,但是对象还没有初始化完成!
3instance(memory);//2.初始化对象
第一个操作 | 第二个操作:普通读写 | 第二个操作:volatile读 | 第二个操作:volatile写 |
---|---|---|---|
普通读写 | 可以重排 | 可以重排 | 不可以重排 |
volatile读 | 不可以重排 | 不可以重排 | 不可以重排 |
volatile写 | 可以重排 | 不可以重排 | 不可以重排 |
以下是关于表格证明的代码
以上是当只加了其中一个作为volatile 时,是否会指令重排
public class Jmm06 {
int a = 0;
private volatile int m1 = 1;
private volatile int m2 = 2;
public void readAndwrite() {
int i = m1;//第一个volatile读
int j = m2;//第二个volatile读
int c = 2;//普通写
m1 = 2;//volatile写 (c与m1此时不会重排)
a = i + j;//第一个普通写(可以重排)(这里涉及了两个volatile和一个普通的写)
m1 = i + 1;//第一个volatile写
m2 = j + 2;//第二个volatile写
}
}