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