-
Java高并发11-伪共享,getUnsafe源码解析并利用反射获取Unsafe实例
一、复习
-
public native long getLongvolatile(Object obj,long offset) -
public native long putLongvolatile(Object obj,long offset,long value) -
void putOrderedLong(Object obj,long offset,long value) -
void park(boolean isAbsolute,long time) -
void unpark(Thread thread) -
long getAndSwapLong(Object obj,long offset,long update) -
long getAndAddLong(Object obj,long offset,long addValue) -
Unsafe使用静态方法getUnsafe()举例
二、getUnsafe源码解析
private static final Unsafe unsafe = new Unsafe();
public static Unsafe getUsafe(){
Class localClass = Reflection.getCallerClass();
if(!VM.isSystemDomainLoader(localClass.getClassLoader())){
throw new SecurityException("Unsafe");
}
return theUnsafe;
}
public static boolean isSystemDomainLoader(ClassLoader paramClassLoader){
return paramClassLoader == null;
}
-
首先调用了getUnsafe这个方法的对象的Class对象,这里就是TestUnsafe.class -
然后判断是不是Bootstrap类加载器的加载的localClass,在这里是看是不是Bootstrap加载器加载了TestUnsafe.class.很明显由于TestUnSafe.class是使用AppClassLoader加载的,所以这里直接抛出了异常。 -
为什么需要这个判断? -
我们知道Unsafe类是rt.jar包提供的,rt.jar包里面的类是使用Bootstrap类加载器加载的,所以在main函数中加载Unsafe类的时候,根据委托机制,会委托给Bootstrap去加载Unsafe类。如果没有这个判断,那么我们的应用程序就可以随意使用Unsafe做事情了,而Unsafe类可以直接操作内存,这是不安全的。(想一想native方法)。所以JDK开发组特意做了这个判断,让开发人员在正规渠道使用Unsafe类,而是在rt.jar包里面的核心类中使用Unsafe功能。
三、使用万能的反射来获取Unsafe实例
package com.ruigege.OtherFoundationOfConcurrent2;
import java.lang.reflect.Field;
import sun.misc.Unsafe;
//import jdk.internal.misc.Unsafe;
public class TestUnsafe2 {
static final Unsafe unsafe;
static final long offset;
private volatile long state=0;
static {
try {
//使用反射获取成员变量theState的值
Field field = Unsafe.class.getDeclaredField("unsafe");
//设置域值为可存取
field.setAccessible(true);
//获取该变量field的值
Unsafe unsafe = (Unsafe)field.get(null);
//获得的这个unsafe类,我们使用它的方法来获取这个测试类的state变量的偏移量
offset = unsafe.objectFieldOffset(TestUnsafe2.class.getDeclaredField("state"));
}catch(Exception e) {
e.printStackTrace();
throw new Error(e);
}
}
public static void main(String[] args) {
TestUnsafe2 testUnsafe2 = new TestUnsafe2();
boolean success = unsafe.compareAndSwapInt(testUnsafe2,offset,0,1);
System.out.println(success);
}
}
-
如上基本在注释中都解释清楚了。
四、Java指令重排
-
Java在会对指令重新排列,如果两个语句之间没有依赖,那么执行哪个在先都行,这种机制在单线程是没有问题的,但是在多线程中会出现问题,他们之间的值相互依赖,就会出现错误的情况 -
对变量是volatile,会避免重排序和内存可见性问题。 -
写volatile变量时,可以保证volatile写之前的操作不会被编译器重排序到volatile写之后。读取volatile变量的时候,可以确保volatile读之后的操作不会被编译器重新排序到volatile读之前
五、伪共享
1.什么是cache
-
在CPU内部会有一二级缓存,它们的存在是因为比直接读取内存更加快捷
2.cache内部如何存储
-
cache内部是按行存储的,其中每一行称为一个cache行。当程序访问变量的时候,会首先去缓存内寻找变量,如果没有找到,那么就去内存中找到这个变量并且把变量的缓存行大小的内存内容都会拿来放到缓存行中。
3.什么叫伪共享
-
一个线程是按照cache行来访问的,因此如果多个变量放到了同一个cache行,并且此时多个线程访问这行里的不同变量,因此线程只能排队修改或取得,我们称之为伪共享。
4.出现的问题
-
CPU1里一级缓存同一cache行有x,y两个变量;CPU2里一级缓存同一cache行也有x,y两个变量 -
此时CPU1修改了x,因此CPU2根据缓存一致性原则,自己一级缓存x失效,y也跟着访问不了,因此CPU2无论修改x还是y都需要去内存重新获取。 -
这说明了多个线程不可能同时去修改自己所使用的CPU中相同缓存行里面的变量。
5.举个例子
int[][] a = new int[1000][1000];
for(int i=0;i<1000;i++){
for(int j=0;j<1000;j++){
a[i][j]=i+j;
}
}
int[][] b = new int[1000][1000];
for(int i=0;i<1000;i++){
for(int j=0;j<1000;j++){
b[j][i]=i+j;
}
}
-
这两个循环赋值,上面的会明显快于下面的,因为数组就是按行存储,上面的就是一行一行赋值的,下面的按列赋值,因此每一个赋值,cache都得获取一个cache行,不同的行来回切换,因此会慢一些
六 、源码:
-
所在包:com.ruigege.OtherFoundationOfConcurrent2 -
https://github.com/ruigege66/ConcurrentJava
-
CSDN:https://blog.csdn.net/weixin_44630050 -
博客园:https://www.cnblogs.com/ruigege0000/
出 处:https://www.cnblogs.com/ruigege0000/p/14055007.html
最新更新
带有参数的装饰器
类装饰器
django中的auth模块与admin后台管理
python的日期处理
字符串常用方法
基本数据类型概述
python-map()函数基本用法
python带你实现任意下载AcFun视频数据~
bbs项目之注册功能
变量的定义和使用
三大常用数据库事务详解之三:事务运行
三大常用关系型数据库事务详解之二:基
三大关系型数据库事务详解之一:基本概
MongoDB常用命令(2)
MongoDB基本介绍与安装(1)
SQLServer触发器调用JavaWeb接口
SQL Server索引的原理深入解析
SqlServer2016模糊匹配的三种方式及效率问题
SQL中Truncate的用法
sqlserver 多表关联时在where语句中慎用tri
VB.NET中如何快速访问注册表
ASP.NET中图象处理过程详解
Vue(1)Vue安装与使用
JavaScript 语言入门
js将一段字符串的首字母转成大写
纯原生html编写的h5视频播放器
H5仿原生app短信验证码vue2.0组件附源码地
TypeScript(4)接口
TypeScript(3)基础类型
TypeScript(2)WebStorm自动编译TypeScript配置