【问题标题】:Setting instance variables of the proxied objects in the InvocationHandler在 InvocationHandler 中设置代理对象的实例变量
【发布时间】:2012-07-19 16:16:42
【问题描述】:

在代理对象(实现java.lang.reflect.InvocationHandler的对象)中,我试图在代理对象中设置一个实例变量。

如下:

public class ServiceProxy implements InvocationHandler {

    private final Object proxiedObject;

    private ServiceProxy(final Object object) {
        this.proxiedObject = object;
    }

    public static Object newInstance(final Object object) {
        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new ServiceProxy(object));
    }


    public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {

        Object result = null;
        MyObject mo = new MyObject();

        // Is the following safe when the proxiedObject is being acceessed by multiple threads?
        final Field sessionField = this.proxiedObject.getClass().getSuperclass().getDeclaredField("mo");
        sessionField.setAccessible(true);
        sessionField.set(this.object, mo);


        result = method.invoke(this.proxiedObject, args);


        return result;
    }
}

这样安全吗?

编辑

实际代码:

Object result = null;
Session session = HibernateUtil.getSessionFactory().getCurrentSession()

// Is the following save when the proxiedObject is being acceessed by multiple threads?
final Field sessionField = this.proxiedObject.getClass().getSuperclass().getDeclaredField("session");
sessionField.setAccessible(true);
sessionField.set(this.object, session);

result = method.invoke(this.proxiedObject, args);
return result;

编辑2: 代理对象是从同时调用同一代理对象的多个方法的 GWT 客户端调用的。发生这种情况时,我让会话实例字段(代理类)以意想不到的方式关闭和打开。

【问题讨论】:

  • 您遇到的实际问题是什么。您在下面暗示了一个“线程问题”,它是什么?
  • 我不明白您所说的“以意外方式关闭和打开”是什么意思。其次,如果您暗示您正在从多个线程使用休眠会话,那么您应该知道休眠会话不是线程安全的。
  • 线程1打开会话,线程1使用打开会话..然后线程2请求打开会话,如果尚未打开..然后线程1关闭会话..然后线程2尝试使用会话(已关闭),因此这里出现异常......
  • 我认为使用SessionFactory 可以解决问题.. 对吗?
  • 是的,每个线程都应该使用自己的 Session 实例(根据需要使用 SessionFactory 获取 Session 实例)。

标签: java multithreading proxy invocationhandler


【解决方案1】:

proxiedObject 被多个线程访问时是否安全?

不,除非movolatile。如果mo 字段是volatile,那么这将正确跨越内存屏障,并且所有线程都可以看到mo 字段的更新。

重要的是要认识到,如果MyObject 不是不可变的,即使movolatile,也会导致额外的并发问题。


编辑:

感谢@jtahlborn cmets。我一直在阅读,现在我很确定volatile保护MyObject 不被部分初始化。

由于constructor instruction reordering possibilities,无法保证MyObject 在其引用在线程之间共享时已被完全构造。只有构造函数中的 final 字段才能保证在构造函数完成时正确初始化。任何其他字段可能已初始化,也可能未初始化,因此您需要先在对象上进行同步,然后多个线程才能开始访问它。

但是,如果mo 字段是可变的,那么“发生在之前”保证还确保MyObject 已完全初始化。在发布volatile 变量之后,不允许编译器重新排序这些指令。见:

【讨论】:

  • 如果mo 字段是可变的,那么由于可变语义,MyObject 将被正确发布。 MyObject 不需要是不可变的或具有所有最终字段或任何其他疯狂的要求。 (很明显,如果mo volatile,那么所有的赌注都被取消了)。
  • jdk 5 内存模型增强了 volatile 以便 nothing 可以在其中重新排序。如果不是这种情况,ReentrantLock、ConcurrentHashMap 和大多数其他新的并发工具将毫无用处。 volatile 现在在内存可见性方面具有相同的同步强度。
  • Synchronization Order,要点 2。那里没有特定于构造函数的内容,因为不需要任何特定内容。易失性写入(对象构造)之前发生的所有事情在易失性读取之后都是可见的。就像在同步块中分配一样。
  • @assylias - 如果不先读取 volatile 引用,您将无法读取另一个线程中的对象字段(显然我假设 volatile 引用是发布对象的方式)。所以它将是“对象(t1)的构造-> w(t1)-> r(t2)->读取对象字段(t2)”(使用t1和t2表示两个单独的线程)。
  • 是的@assylias。我认为执行顺序不可能是:对象构造-> w-> 读取对象字段-> r。能够读取volatile变量之前读取对象字段的唯一线程将是第一个不会有问题的线程。
猜你喜欢
  • 2018-07-18
  • 1970-01-01
  • 1970-01-01
  • 2014-02-25
  • 1970-01-01
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多