为了不深入了解我的软件应该做的事情,让我举一个我想要解决的例子,让这个简短而甜蜜。
假设我有一个名为X的基类和该类的实现,我将调用Y.类Y,当然,扩展基类X.假设我有20个对象将通过单独的线程实例化类Y对于每个对象,每个实例化都会将一个大文件加载到内存中。其中一些对象可能需要使用不同的文件,但为了简化这一点,我们可以说它们都需要访问同一个文件。
有没有办法定义在基类中静态指向这些文件的某个对象(变量),这样即使实现类通过20个不同的线程加载了20次,它们也可以共享同一个静态对象,这样文件只需要加载一次???
感谢您的帮助...
答案 0 :(得分:5)
如果是这样而且String
只是使它成为protected static final String
并且它是线程安全的。如果它是可变的,你将来会受到伤害。
如果它是二进制文件并且只能以只读方式使用,那么您可以使用byte[]
代替String
执行相同的操作,并确保不要让它任何事情都会改变数组中的字节。更好的方法是以只读方式实现某个Stream
或Reader
接口。
使线程安全的最简单和最安全的方法是使其不可变。 final
关键字使引用成为不可变的,它不会使它指向的对象成为不可变的。由于String
是不可变的,final
使引用也成为不可变的,你很高兴。如果您需要在所有线程之间共享更改的可变性,java.util.concurrent
包将是您的朋友。
如果你创建变量protected static final
那么子类的所有实例,无论它们所处的执行线程如何,都会看到数据。
答案 1 :(得分:1)
您可以先使用ConcurrentHashMap。
开始为地图创建一个字符串键,值应该是加载的表示必须是的。
请注意,如果更改加载的文件数据,即使使用ConcurrentHashMap,仍需要确保线程安全。
在创建对象之前初始化此映射并将其传递给对象的构造函数。
答案 2 :(得分:1)
如果您提前知道该文件,则可以在静态初始化程序块中打开并加载该文件,并将内容存储在静态数据成员中。然后,无论当前访问实例对象的线程是什么,都可以访问该类的所有实例的内容。
// In the base class
protected static final String fileContents;
static {
fileContents = readStuffFromFile();
}
答案 3 :(得分:0)
创建一个单独的对象来存储文件的缓存内容。
通过同步使此对象在必要时具有线程安全性,以便多个线程可以访问此对象。在基类X中,添加对此对象的引用。现在,可以使用相同的缓存对象实例化多个类X的实例。现在要求每个文件只加载一次该对象,并且可以根据需要在多个X / Y对象之间共享该对象。
剩下的唯一问题是有一种加载这些文件的方法。解决方案将取决于您的应用程序和这些文件的结构,但我将提供一种可能的解决方案。
创建一个工厂类,它将创建这种新类型的对象。这个工厂将在自己的线程上运行,所有加载的文件都将通过这个工厂加载。创建一个可以从此工厂请求文件的接口。工厂保留对所有已加载文件的引用,因此如果它已经加载,它可以立即返回引用。如果未加载,则在与此文件相关的工厂中存储的占位符对象上使用Object.wait()
阻止进行调用的线程。一旦工厂加载完文件,就在该文件的占位符对象上调用Object.notifyAll()
,该文件将唤醒每个线程,这些方法将返回并引用加载的文件。
一旦完成,每个需要文件的线程都可以在工厂调用该方法来获取文件对象。此线程现在将阻塞,直到加载文件对象,然后该函数将返回。只要这是可以的,它似乎应该是因为那些线程将等待文件加载,然后这个解决方案应该运行良好。
答案 4 :(得分:-2)
非静态内部阶级将满足您的所有愿望:
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票后):
首先,上述解决方案都没有解决原始问题的部分,即所有对象可能没有共享1个文件。一组对象可能需要共享文件A,另一组文件B,等等。上面的内部类解决方案旨在满足该要求。您为每个文件/组实例化外部类,并从同一外部对象实例化组的内部对象。
其次,静态是一个糟糕的选择:很可能以后需要在运行时而不是在程序启动时指定文件。上面的外部/内部类结构正好解决了这个问题。您可以在需要时实例化外部类。不需要静态初始化(也没有任何复杂的延迟静态初始化方案)。
第三,线程偏执在这个问题(或这个解决方案)中根本不是问题。很明显,该文件是只读的,因此是不可变的,因此对问题的所有并发只会减损优雅的解决方案。
最后,谈到优雅,这个是,也可能是唯一的。
这个更新主要是针对一个新来的人来看这个帖子,因为这个帖子中的否定选民可能会把这个提升到-5。