How to improve Java performace using static variables and threads?
For the sake of not going too deep into what my software is supposed to do let me just give an example of what i am trying to solve, to make this short and sweet.
Lets say i have a Base Class called X and an implementation of that class, i will call Y. Class Y, naturally, ex开发者_StackOverflow社区tends Base Class X. Lets say I have 20 objects that will be instantiating Class Y via a separate thread for each object and with every instantiation a big file is loaded into memory. Some of these objects, perhaps, might need to use different files but to make this simple, lets say they all need access to the same file.
Is there a way to define a certain object(variable) that points to these files statically in the base class so that, even though the implementation class is loaded 20 times via 20 different threads, they all can share the same static object, so that the file only needs to be loaded one time???
thanks for your help in advance...
- is that file read-only?
- is it a big string of data?
if so and a String
just make it a protected static final String
and it is thread safe. if it is mutable you have a whole world of hurt in your future.
if it is a binary and will only be used in a read-only manner you can probably do the same thing with a byte[]
in place of the String
and make sure you don't let anything change the bytes in the array. A better way would be to implement some Stream
or Reader
interface in a read-only manner.
the simplest and safest way to make something thread safe is make it immutable. the final
keyword makes references immutable, it doesn't make the object it points to immutable. Since a String
is immutable the final
makes the reference immutable as well and you are good to go. If you need mutability with the changes shared amongst all the threads, the java.util.concurrent
package will be your friend.
If you make the variable protected static final
then all instances of the subclass regardless of the thread of execution they are on will see the data.
If you know the file ahead of time, you could open and load the file in a static initializer block, and store the contents in a static data member. Then the content will be accessible for all instances of that class, regardless of what thread is currently accessing the instance objects.
// In the base class
protected static final String fileContents;
static {
fileContents = readStuffFromFile();
}
You can start by using ConcurrentHashMap.
Make a key to the map a string and the value should be whatever the loaded representation must be.
Note that if you change the loaded file data you still need to ensure thread safety even if you are using ConcurrentHashMap.
Initialize this map before creating your objects and pass it to the object's constructor.
Create a separate object to store the cached contents of the file.
Make this object thread-safe as necessary through synchronization so that multiple threads can access this object. In your base class X, put a reference to this object. Now, multiple instances of class X could be instantiated with the same cached object. This now requires that this object only be loaded once per file and the object can be shared across as many X/Y objects as necessary.
The only problem that remains is having a method of loading these files. The solution to this will depend upon the structure of your application and these files, but I will offer one possible solution.
Create a factory class which will create objects of this new type. This factory will run on its own thread, and all files loaded will be loaded through this factory. Create an interface where a file can be requested from this factory. The factory keeps a reference to all files that are loaded, so if it's already loaded it can just immediately give the reference back. When it's not loaded, block the thread making the call using Object.wait()
on a placeholder object stored in the factory related to this file. Once the factory is done loading the file, call Object.notifyAll()
on the placeholder object for that file which will wakeup each thread and those methods will return with the reference to the loaded file.
Once this is complete, each thread which needs a file can just call the method on the factory to get the file object. This thread will now block until the file object is loaded and then the function will return. As long as this is okay, which it seems it should be since those threads will be waiting for the file to load anyways, then this solution should work well.
a non-static inner class will fulfill all your desires:
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();
}
}
Well, well, well, (-2 votes later):
Firstly, none of the above solutions address the part of the original question that there may not be exactly 1 file shared by all objects. One group of objects may need to share file A, and another group file B, and so forth. The inner class solution above is intended to fulfill exactly that requirement. You instantiate the outer class once per file/group, and you instantiate inner objects for a group from the same outer object.
Secondly, static is a poor choice: it is quite likely that the file might need to be specified later during the run-time rather than at program startup. The outer/inner class structure above addresses exactly that issue. You instantiate the outer class whenever you need to. No static initialization is needed (nor any complicated schemes for deferred static initialization).
Thirdly, thread paranoia was simply not an issue in this problem (or this solution). It is pretty clear that the file is a read-only, hence immutable, so going all concurrent on the problem will only detract from elegant solutions.
Finally, speaking of elegant, this one is, and probably the only one.
This update is mostly for someone new who comes and looks at the thread, since the negative voters in this thread will probably get this to -5.
精彩评论