【问题标题】:How to improve Java performace using static variables and threads?如何使用静态变量和线程提高 Java 性能?
【发布时间】:2011-02-17 22:52:46
【问题描述】:

为了不深入探讨我的软件应该做什么,让我举一个我正在尝试解决的问题的例子,以使这个简短而有趣。

假设我有一个名为 X 的基类和该类的实现,我将调用 Y。Y 类自然扩展了基类 X。假设我有 20 个对象将通过单独的线程实例化 Y 类对于每个对象,每次实例化都会将一个大文件加载到内存中。其中一些对象可能需要使用不同的文件,但为了简单起见,假设它们都需要访问同一个文件。

有没有办法在基类中定义一个静态指向这些文件的对象(变量),这样,即使实现类通过 20 个不同的线程加载 20 次,它们都可以共享同一个静态对象,这样文件只需要加载一次???

提前感谢您的帮助...

【问题讨论】:

  • 你需要的基本上是一个缓存。

标签: java multithreading performance variables static


【解决方案1】:
  1. 那个文件是只读的吗?
  2. 是一大串数据吗?

如果是这样,String 只需将其设为protected static final String,它是线程安全的。如果它是可变的,那么你的未来就会受到整个世界的伤害。

如果它是二进制文件并且只能以只读方式使用,您可能可以用byte[] 代替String 做同样的事情,并确保不要让任何东西改变字节在数组中。更好的方法是以只读方式实现一些StreamReader 接口。

使线程安全的最简单和最安全的方法是使其不可变。 final 关键字使引用不可变,它不会使其指向的对象不可变。由于 String 是不可变的,final 也使引用不可变,您可以继续使用。如果您需要在所有线程之间共享更改的可变性,java.util.concurrent 包将是您的朋友。

如果您将变量设为protected static final,则子类的所有实例(无论它们所在的执行线程如何)都会看到数据。

【讨论】:

  • 它实际上是一个ShapeFile,您可以在其中打开一个.shp 部分和一个.dbf 部分。我正在使用 OpenMap 来操作这些文件。
  • 唯一真正需要考虑的是数据的只读/不可变状态,如果它是不可变的,那么你就是线程安全的,否则就是痛苦的世界。 . .
【解决方案2】:

如果您提前知道文件,您可以在静态初始化程序块中打开并加载文件,并将内容存储在静态数据成员中。然后,该类的所有实例都可以访问内容,而不管当前正在访问实例对象的线程是什么。

// In the base class
protected static final String fileContents;

static {
    fileContents = readStuffFromFile();
}

【讨论】:

  • 问题仍然存在,该类的所有实例或该类的所有线程实例都可以访问,每个实例都有自己的线程。我想这就是我的问题的真正意义所在。是能够“看到”基类中相同静态方法或对象(变量)的不同线程的对象。
  • 需要被保护以便子类可以访问它!
  • @usmsci:是的,派生类的所有对象,无论线程如何,都可以看到其基类的相同静态成员,前提是所讨论的成员至少是包范围(如果派生类在同一个包中)或至少是受保护的范围(如果派生类在不同的包中)并且受制于静态成员的正确线程安全初始化。此答案中的代码需要将private 更改为protected
  • 感谢 cmets。我已将静态字段更新为受保护。
  • @usmsci 对象不“属于”线程。多个线程很可能访问同一个对象。使用ThreadLocal 可以为每个线程创建不同的变量副本,但我认为这不是您感兴趣的内容。Java Concurrency in Practise 是一本关于并发的优秀书籍。
【解决方案3】:

您可以先使用ConcurrentHashMap

将映射的键设为字符串,值应为加载的表示形式。

请注意,如果您更改加载的文件数据,即使您使用的是 ConcurrentHashMap,您仍然需要确保线程安全。

在创建对象之前初始化此映射并将其传递给对象的构造函数。

【讨论】:

  • 谢谢 - 我会立即调查。我已经查看了关键字同步,也许声明了在基类中定义的同步静态方法,该方法将跟上已加载的文件,但我不确定这是否会允许所有线程对象查看文件详细信息。 - 再次感谢!
  • 如果数据不可变,则不需要synchronized
【解决方案4】:

创建一个单独的对象来存储文件的缓存内容。

根据需要通过同步使该对象成为线程安全的,以便多个线程可以访问该对象。在您的基类 X 中,放置对该对象的引用。现在,可以使用同一个缓存对象来实例化 X 类的多个实例。现在,这要求每个文件只加载此对象一次,并且可以根据需要在任意数量的 X/Y 对象之间共享该对象。

剩下的唯一问题是加载这些文件的方法。解决方案取决于您的应用程序和这些文件的结构,但我将提供一种可能的解决方案。

创建一个工厂类,它将创建这种新类型的对象。这个工厂会在自己的线程上运行,所有加载的文件都会通过这个工厂加载。创建一个可以从该工厂请求文件的接口。工厂保留对所有加载文件的引用,因此如果它已经加载,它可以立即返回引用。未加载时,阻止使用Object.wait() 对存储在与此文件相关的工厂中的占位符对象进行调用的线程。工厂加载完文件后,在该文件的占位符对象上调用 Object.notifyAll(),这将唤醒每个线程,这些方法将返回对加载文件的引用。

一旦完成,每个需要文件的线程都可以调用工厂的方法来获取文件对象。这个线程现在将阻塞,直到文件对象被加载,然后函数将返回。只要这没问题,看起来应该是这样,因为这些线程无论如何都会等待文件加载,那么这个解决方案应该可以很好地工作。

【讨论】:

    【解决方案5】:

    一个非静态的内部类将满足你所有的愿望:

    public class Foo {
      protected String member;
      public Foo(String member) {
        this.member = member;
      }
      public class Bar {
        protected String member;
        public Bar(String member) {
          this.member = member;
        }
        public void show() {
          System.out.println("this.member: " + this.member + "; Foo.this.member: " + Foo.this.member);
        }
      }
      public static void main(String[] args) throws javax.mail.MessagingException, java.io.IOException {
        Foo foo_a = new Foo("a");
        Foo foo_b = new Foo("b");
        Bar bar_a1 = foo_a.new Bar("1");
        Bar bar_a2 = foo_a.new Bar("2");
        Bar bar_b1 = foo_b.new Bar("1");
        Bar bar_b2 = foo_b.new Bar("2");
        bar_a1.show();
        bar_a2.show();
        bar_b1.show();
        bar_b2.show();
      }
    }
    

    好吧,好吧,好吧,(-2票后):

    首先,上述解决方案都没有解决原始问题的一部分,即所有对象可能不完全共享一个文件。一组对象可能需要共享文件 A,而另一组对象可能需要共享文件 B,依此类推。上面的内部类解决方案旨在完全满足该要求。您为每个文件/组实例化一次外部类,然后从同一个外部对象为组实例化内部对象。

    其次,静态是一个糟糕的选择:很可能需要在运行时稍后而不是在程序启动时指定文件。上面的外部/内部类结构正好解决了这个问题。您可以在需要时实例化外部类。不需要静态初始化(也不需要任何复杂的延迟静态初始化方案)。

    第三,线程偏执在这个问题(或这个解决方案)中根本不是问题。很明显,该文件是只读的,因此是不可变的,因此对问题进行所有并发只会有损于优雅的解决方案。

    最后,说到优雅,这一款是,也可能是唯一一款。

    此更新主要针对新来并查看线程的人,因为此线程中的负面投票者可能会将其变为 -5。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-21
      • 1970-01-01
      • 2012-01-15
      • 2012-05-18
      • 1970-01-01
      • 1970-01-01
      • 2013-03-29
      相关资源
      最近更新 更多