2

A让我们考虑一个具有以下特征的第三方类:

  1. 它的大部分实现是私有的并且相当复杂,因此可以扩展但不能合理修改。

  2. A有一个非常昂贵的构造函数,永远不应该被不必要地调用——在我的例子中,它构建了一个巨大的数据查找表供以后使用。

    这意味着创建一个装饰器类作为替代A是不可能的,因为任何装饰器都必须继承A并调用它的构造函数。

  3. A由第三方代码使用,因此不能在其位置使用代理类A- 其他代码需要实例,这就是它应该得到的。

  4. A提供了一堆非常昂贵的报告方法,这些方法可以解决很多问题生成一堆报告。

    这些方法不会产生任何本地使用的结果——理论上它们可以是异步的,但不幸的是它们不是。

  5. A互相调用的方法- 很多。

  6. A实现是不透明的并且不断变化 - 像使用反射后实例化来例如缩小事物这样的黑客是不可能的。

  7. A不是线程安全的 - 不是真的......

我达到了成为我项目中的瓶颈的地步A,所以我创建了一个子类B,希望将实际的乱码到一个单独的线程中。我覆盖了所有公共方法,A只将Runnable对象提交给单线程执行器服务。然后每个从工作线程Runnable执行适当的方法。super

不幸的是,它调用了很多A自己的公共方法。通常这不会是一个问题,但在我的情况下,超类是从. 这一个主要问题,因为这些方法会延迟实际执行并将新任务提交给执行器服务,从而导致性能和正确性问题。BB

我的解决方案是检查Thread.currentThread()所有方法的结果,B以便来自工作线程的调用将直接委托给该super方法,而不是作为新任务提交。

所以现在我正处于它已经成为一个性能问题的地步Thread.currentThread(),可能是因为它是一种经常被调用的本机方法。

  • 是否有更快的方法以线程安全的方式检查源自超类和/或工作线程的方法调用?

  • 是否有替代设计可以让我将A' sfrabbling of wrabbles推到另一个线程?在我看来,由于A是第三方代码,我大多被困......

4

3 回答 3

1

是否有更快的方法以线程安全的方式检查源自超类和/或工作线程的方法调用?

您可以替换Thread.currentThreadThreadLocal。只需设置例如 a和 if 设置(BooleanThreadLocal输入方法时检查它),不要产生新线程,而是在同一个线程上执行。

更新
使用哈希图来跟踪方法调用。进入方法后,检查它是否存在于哈希图中(方法的名称作为键)。
如果它不存在,则放入 hashmap 并使用后台线程调用该方法。
如果它存在,那么您必须已经在后台线程中。所以在同一个线程中调用

于 2013-02-10T18:09:57.320 回答
0

如果您的运行时环境允许使用自定义类加载器,您应该能够将第三方类 A 替换为使用反射调用原始类 A 的自定义委托类。

这是委托类 A 的示例:

package fi.test;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ClassA {
    private static final Class<?> clazz;
    private static final Method doSomething;
    private Object instance;

    static {
        ClassLoader classLoader = new ClassLoader(ClassA.class.getClassLoader()) {
            @Override
            public synchronized Class<?> loadClass(String name)
                    throws ClassNotFoundException {
                if (!name.equals(ClassA.class.getName())) {
                    return super.loadClass(name);
                }
                Class<?> clazz = findLoadedClass(name);
                if (clazz == null) {
                    // The original class A is stored as ClassA.class.orig
                    InputStream inputStream = getParent().getResourceAsStream(
                            "fi/test/ClassA.class.orig");
                    try {
                        byte[] buffer = new byte[100000];
                        int length = inputStream.read(buffer);
                        clazz = defineClass(name, buffer, 0, length);
                    } catch (IOException exception) {
                        throw new ClassNotFoundException("", exception);
                    } finally {
                        try {
                            inputStream.close();
                        } catch (IOException exception) {
                        }
                    }
                }
                return clazz;
            }
        };
        try {
            // Class A
            clazz = classLoader.loadClass(ClassA.class.getName());
            // Do something
            doSomething = clazz.getMethod("doSomething", new Class<?>[0]);
        } catch (Exception exception) {
            throw new Error(exception);
        }
    }

    public ClassA() {
        try {
            instance = clazz.newInstance();
        } catch (InstantiationException exception) {
            throw new RuntimeException(exception);
        } catch (IllegalAccessException exception) {
            throw new RuntimeException(exception);
        }

    }

    public void doSomething() {
        try {
            doSomething.invoke(instance, new Object[0]);
        } catch (IllegalAccessException exception) {
            throw new RuntimeException(exception);
        } catch (InvocationTargetException exception) {
            throw new RuntimeException(exception);
        }
    }
}

这是原来的 A 类:

package fi.test;

public class ClassA {
    public void doSomething() {
        System.out.println("This is magic!");
    }
}

这是我用来测试委托的类:

package fi.test;

public class Tester {
    public static void main(String[] args) {
        ClassA instance = new ClassA();
        instance.doSomething();
    }
}
于 2013-02-10T22:32:43.397 回答
0

由于A不是线程安全的,因此用不同的线程调用它不是……安全的。

想法:

  • 您可能正在池化 的实例A,即每当一个线程需要 A 时,它会从池中获取一个(如果池为空,则创建一个),使用它,然后将其放回池中。这样,您将同时拥有所需数量的 A 实例。
  • 您可以修补A以跨实例缓存“巨大的查找表”(当然,这要求它是线程安全的,但是如果仅在构造后读取该表,则情况很简单),并使用Ain的新实例每个线程。
于 2013-02-10T18:09:09.413 回答