spring单例在高并发下可能出现的错误. spring单例在高并发下可能出现的错误: 首先,只有当注入的对象是无状态的幂等的才可以保证执行前后不被修改,否则执行一次之后单例对象就会发生改变,在下次执行有肯能造成结果不一样,当在高并发的情况下就会出现,这个线程刚使用单例对象进行属性设置,还未使用的情况下,另一个进程已经将单利对象的数据进行修改属性完成,则远来线程获取到的单例就是一个脏对象不可使用.。ps:上面代码中还有两个地方要注意:一个地方是$exampledo.getitemlist()代码被解析成_i.exampledo).getitemlist()方法调用(第一次编译时是通过反射调用,多次编译后通过方法调用)java反射机制,也就是将velocity的动态反射调用变成了java的原生方法调用。tryenter重载方法 作用:该方法的意思是,尝试着获取资源的互斥锁,在指定的时间内(不指定就默认无限大,阻塞)获取了就返回true执行下面的临界区代码(这个方法调用后和释放锁之间的代码都是临界区代码),否则,返回false,执行下面的代码(没有获取到锁,下面的代码不是临界区代码)。
@CallerSensitive
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}
然后,看下获取MethodAccessor方法,首先也是从缓存中获取,如果获取不到就调用reflectionFactory.newMethodAccessor方法获取一个并设置缓存
private MethodAccessor acquireMethodAccessor() {
MethodAccessor tmp = null;
if (root != null) tmp = root.getMethodAccessor();
if (tmp != null) {
methodAccessor = tmp;
} else {
tmp = reflectionFactory.newMethodAccessor(this);
setMethodAccessor(tmp);
}
return tmp;
}

然后,具体看下newMethodAccesor方法,该方法可以使用inflation机制创建accessor,初始的时候noInfalation值为false,所以开始的时候会调用NativeMethodAccessorImpl的方法invoke。
private static boolean noInflation = false;
private static int inflationThreshold = 15;
public MethodAccessor newMethodAccessor(Method method) {
checkInitted();
if (noInflation) {
return new MethodAccessorGenerator().
generateMethod(method.getDeclaringClass(),
method.getName(),
method.getParameterTypes(),
method.getReturnType(),
method.getExceptionTypes(),
method.getModifiers());
} else {
NativeMethodAccessorImpl acc =
new NativeMethodAccessorImpl(method);
DelegatingMethodAccessorImpl res =
new DelegatingMethodAccessorImpl(acc);
acc.setParent(res);
return res;
}
}
再次,看下NativeMethodAccessorImpl的实现,这里的++numInvocations就是统计次数的,有源码可以看出,前15次会调用native的invoke0方法,这种方法初始化比较快,但性能不够好。而过了15次之后都是底层调用asm给Method生成字节码然后加载到内存形成对象进行调用的,这种方式初始化较慢,但性能较好。
class NativeMethodAccessorImpl extends MethodAccessorImpl {
private Method method;
private DelegatingMethodAccessorImpl parent;
private int numInvocations;
NativeMethodAccessorImpl(Method method) {
this.method = method;
}
public Object invoke(Object obj, Object[] args)
throws IllegalArgumentException, InvocationTargetException
{
if (++numInvocations > ReflectionFactory.inflationThreshold()) {
MethodAccessorImpl acc = (MethodAccessorImpl)
new MethodAccessorGenerator().
generateMethod(method.getDeclaringClass(),
method.getName(),
method.getParameterTypes(),
method.getReturnType(),
method.getExceptionTypes(),
method.getModifiers());
parent.setDelegate(acc);
}
return invoke0(method, obj, args);
}
void setParent(DelegatingMethodAccessorImpl parent) {
this.parent = parent;
}
private static native Object invoke0(Method m, Object obj, Object[] args);
}
最后,来看下asm生成字节码方法的实现,这个方法的最后返回了一个MagicAccesorImpl(开始是没有实现的空类),其中的AccessController.doPrivileged(new PrivilegeAction)相当于执行一个任务,该任务调用ClassDefiner的defineClass方法,该方法内部同样也是执行任务,但内部的方法每次执行的时候都会返回一个类加DelegatingClassLoader类加载器,然后把类的一些字节码信息连同这个类加载器一起交给unsafe让他去把字节码通过类加载器加载到内存里面来以MagicAccessorImpl的类形式存在与内存中。
return (MagicAccessorImpl) AccessController.doPrivileged(new PrivilegedAction() {
public MagicAccessorImpl run() {
try {
return (MagicAccessorImpl) ClassDefiner
.defineClass(arg12, arg16, 0, arg16.length, arg0.getClassLoader()).newInstance();
} catch (IllegalAccessException | InstantiationException arg1) {
throw new InternalError(arg1);
}
}
});
class MagicAccessorImpl {
}
class ClassDefiner {
static final Unsafe unsafe = Unsafe.getUnsafe();
static Class<?> defineClass(String arg, byte[] arg0, int arg1, int arg2, final ClassLoader arg3) {
ClassLoader arg4 = (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
public ClassLoader run() {
return new DelegatingClassLoader(arg3);
}
});
return unsafe.defineClass(arg, arg0, arg1, arg2, arg4, (ProtectionDomain) null);
}
}
我们需要实现一个defaultactioninvoker以默认的方式进行方法的调用,也就是找到方法的一些ifilter按照一定的顺序执行他们,最后使用反射进行方法的调用得到上面说的iactionresult并执行它的execute()方法。调用线程类的start()方法使该线程开始执行。当a线程调用anyobject对象加入synchronized关健字的x方法时,a线程就获得了x方法所在对象的锁,所以其他线程必须等a线程执行完毕才可以调用x方法,而b线程如果调用声明了synchronized关键字的非x方法时,必须等a线程将x方法执行完,也就是释放对象锁后才可以调用。
Method method = clazz.getDeclaredMethod("methodReflected");
method.invoke(target);
@CallerSensitive
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}
的执行结果是:___52____ 答:date是一个union, 变量公用空间. 里面最大的变量类型是int[5], 占用20个字节. 所以它的大小是20。

image.png

image.png
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-111385-3.html
为了他的电商事业