aspose file tools*
The moose likes Developer Certification (SCJD/OCMJD) and the fly likes RMI and Threading Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Certification » Developer Certification (SCJD/OCMJD)
Bookmark "RMI and Threading" Watch "RMI and Threading" New topic
Author

RMI and Threading

Matt Pavlovich
Ranch Hand

Joined: Aug 14, 2010
Posts: 98
I hope this isn't a stupid question, but I am going to post it anyway in hopes of understanding things a little better.

My create() uses the writeLock() to ensure thread safety. However, if from another class I call pool.submit(<object reference here>) on a run() containing a call to the create(), I still am seeing that unless I use a synchronize block to surround the logic in the run(), that not all my threads are getting their work done when I surround the call to the method in a synchronized block. In other words, if I use a loop to call pool.submit 30 times, only 7 new records are written...sometimes less sometimes more. This problem does not occur when I put the run() 's logic in a synchronized block. So, I am hoping someone can explain why, even though my create() "appears" to be thread safe with the use of the writeLock(), that this is happening.

Here is a simplified code example just for illustration purposes:


So when this code is wrapped in a synchronized block, all is good. However, when it is not, there is trouble with getting all 30 new records to be added to the database. It would seem that since my create method is surrounded by the writeLock() that this should not be happening. But apparently my understanding is lacking. I understand I will not have to use this method in my GUI, but I think understanding this will help me understand other concepts in threading that I will need for the test and beyond.

Thank you, guys, for your help.
Matt
Roel De Nijs
Bartender

Joined: Jul 19, 2004
Posts: 5266
    
  13

Hi Matt,

To be honest: sounds to me your create-method is not thread-safe at all Seems like a bunch of records are not written to the file (or are overwritten by other records) which results in +- 7 new records.

If your Data class would be thread-safe it doesn't matter how many threads are creating records, you'll end up with as many records as you created. In this discussion it's important to have more details about the setup of your Data class.

Could you try the following scenario? If you create just one (a singleton) instance of your Data class (which will be used to perform all creates on), you can make the create-method synchronized (instead of using writeLock). You should have in the end 30 new records.
If you create more than 1 instance of Data class (e.g. 1 per thread) you should of course lock on an object which is shared between the different Data instances, otherwise you don't have thread-safety at all.

And you are correct that the create-method is not needed from the GUI, but of course in the Data class you should have implemented this method correctly. So starting 30 threads all creating 1 record must result in 30 new records in your database file. So it's important that you can solve this issue.

Kind regards,
Roel


SCJA, SCJP (1.4 | 5.0 | 6.0), SCJD
http://www.javaroe.be/
Matt Pavlovich
Ranch Hand

Joined: Aug 14, 2010
Posts: 98
Roel,

Thanks very much. That did help a great deal. Now the create() works as it should.

That being said, I am using the SCJD book to help me through the RMI. They say that if I don't use an RMI factory, which creates a new Data object each time, that I run the risk of thread re-use (but I am not really sure why that is a problem if everything is locked as it should be). If I make my Data class a singleton (which works) it seems to contradict their way (which does not work for me). So, I guess the question is now - if I am using a singleton pattern, is it a waste to have an RMI factory? It sort of seems that way to me.

Also, I don't know if it is a good design pattern, but I am tinkering with the idea of making my CreateCommand class (and the others) all runnable objects, so that a separate thread takes care of each of these operations, even if there is only one user. I am pretty sure I can make this work quite easily, but I am not sure if this is considered a "bad practice". In theory, I like it. What do you think?

Thank you as always for your insight.
Matt
Roel De Nijs
Bartender

Joined: Jul 19, 2004
Posts: 5266
    
  13

Hi Matt,

Using a book is of course a good thing, but the most important thing is understanding what the book tells you before you apply it to your own situation (application).

As you know (or should know) is that RMI does not give you the guarantee that subsequent calls by the same client are handled by the same thread on the server. So if you need a way to identify the client uniquely you can't use the thread-id. One way to solve this issue is, like in the book, make use of a rmi factory. If you have a lockCookie in your interface, you simply don't need this unique identification, because you already have one (the lockCookie itself), so using this rmi factory is not needed. In my assignment the interface didn't have a lockCookie, so instead of this RMI factory I used another way to identify a client uniquely.

It's hard to give an opinion about the CreateCommand with little information. Is a CreateCommand (and other ones too) created in your business service or in your Data class or somewhere else? Just know when you use a rmi server this server manages for you a pool of threads to handle all incoming requests, so why would you care about spawning a bunch of other threads?

Hope it helps (as always )
Kind regards,
Roel
Matt Pavlovich
Ranch Hand

Joined: Aug 14, 2010
Posts: 98
Roel,

Good point about the book. I had thought it was strange it was talking about keeping track of the client when we ourselves were using a lockCookie for that very thing. I guess it's fair to say I should not expect the book to be an exact map for the test.

As far as RMI goes, I (sadly) did not know it created a pool of threads. I though I would have to do that myself. I get that now, though. And now that I understand that, my question about making CommandClasses, runnable is mute. (Just to answer your question, these clases are in my .db package.) Sorry that I don't post more code from these classes when asking questions. I just don't want to be the guy that posts 100 lines of code and then expects everyone to read it Thus, I try to simplify it as much as possible.

As always, thank you.
Matt
Roel De Nijs
Bartender

Joined: Jul 19, 2004
Posts: 5266
    
  13

Hi Matt,

I think it was not easy to create the book, because you want to address all possible difficulties in the assignment, but you don't want to give it all away, so a reader can just copy/paste the code from the book and pass the assignment. Andrew did a great job, I found his book very useful and I used some concepts of it, but always thought about them before applying to my application. It even could be that I copy/pasted a limited amount of code, but every line is put to some questions: "what's the purpose of this line of code", "do I really need this line of code", "could I not adjust this line of code to make it even better",... I remember the configuration settings panel: I used the same approach, but in the book some fields are validated, others are not. In my assignment all fields are validated and if you have entered an invalid value, you will not be able to proceed.
And to prevent just copy/paste from the book Andrew deliberately violated one of the must requirements. So if you just are copy/pasting and do not know what you actually are doing, you'll fail automatically.

That's one of the advantages of using RMI (vs sockets): you don't have to do thread management to handle incoming requests, it's done for you RMI has a lot of advantages (also some disadvantages, like any choice you have to make) and you'll have to justify your decision in choices.txt, so make sure you don't forget that and you can list a few (in my document I listed 8 pros and 3 cons).

Don't worry: if you should post 100 lines of actual code, it will be removed from your post, because that's not allowed You could also use some (method) signatures and have implementation in comments for example, just to give an idea of your setup of classes. Like:


Matt Pavlovich wrote:Thus, I try to simplify it as much as possible.

That's the spirit
Matt Pavlovich
Ranch Hand

Joined: Aug 14, 2010
Posts: 98
Hey Roel,

Yes, not to worry...I am not just copying and pasting from the book. That would be test suicide. But for something like RMI, that I had never used until this point, I am (or was) pretty much at the mercy of whatever I am reading and my ability to extrapolate its lesson to my own work. Thankfully, though, you guys on the forum are always very helpful in clarifying issues that I have not been able to comprehend on my own.

Thanks again.
Matt
Matt Pavlovich
Ranch Hand

Joined: Aug 14, 2010
Posts: 98
Hey Roel,

I do actually have more more (hopefully simple) question if you don't mind. In the SCJD book, when he is showing a test of the RmiNoFactory class, which calls two threads, he uses sleep(1000); between the start of each thread.



Is there a particular reason for this? If everything is locked in the SCJD book code (which it is), then is that sleep() necessary? I ask because when I do my own thread test (without the sleep()), with the run() containing both a call to create() to create a new record and a call to the read() to read a record, I am getting an IndexArrayOutOfBounds exception. My thought is that my problem is I have more than one instance of my RandomAccessFile object, (one in the CreateCommand class, and one in the ReadCommand class) both with the same reference variable. I am 99% sure that is what is causing my problems since they are two different objects (even though they point to the same datafile). However, before I re-arrange everything, I wanted to make sure that I am not missing some simple threading detail that is being demonstrated in the book.

Thanks for your help.
Matt
Roel De Nijs
Bartender

Joined: Jul 19, 2004
Posts: 5266
    
  13

Hallo Matt,

I don't have the book you are referring too (I used a copy of the former company I worked for and had to hand in that copy of course when I quit my job and started my own company), so I don't have any idea about why the sleep is in there. I even don't have an idea what the RmiNoFactoryExample is doing (or is supposed to do )

Regarding your issue about creating and reading, I guess you refer to a code snippet like this:

and you get an IndexArrayOutOfBoundsException when trying to read the newly written record.

I'm a bit confused about this IndexArrayOutOfBoundsException. That almost implies you are using an ArrayList (or something like that) as a record cache. Is this record cache built every time the ReadCommand is invoked or just once when it is created Oh wait, memory is coming back... you are probably that guy that rebuilds the ArrayList when calling read-method, before returning the appropriate record array. And sounds to me you are back at this problem.
I'm still wondering what's the benefit of using these commands, because they add an extra layer of complexity, but I don't see any pros, just a few cons (this IndexArrayOutOfBoundsException for example)? Each command has it's own RAF instance. Are these instances opened and closed at the beginning and end of each run of a command (or are they kept open and closed when program is shutdown)? If you (flush and) close your RAF in the create-command and open the RAF in your read-method to rebuild the ArrayList I don't think that should be a problem. Of course using different RAF instances adds again an extra complexity which is not needed and has no benefits (just extra problems).

Kind regards,
Roel
Matt Pavlovich
Ranch Hand

Joined: Aug 14, 2010
Posts: 98
Hey Roel,

No big deal about the book code. I figured out my code mess with the ArrayList and all that went away. Now it creates and reads simultaneously as it should. You were were right, it was in indeed the recalculating of the record ArrayList with arrays and then populating those arrays each time I needed to read it that was causing problems. I see now how this is not very efficient. It is, however, all better now. I still need to make it more efficient but I will get to that after I tackle this other monster...

The ReadAccessFile instances are another matter and I am having a tough time figuring out what the problem is. You are correct in stating that I have a command class for each type of file manipulation. They are CreateCommand, ReadCommand, FindCommand, and DeleteCommand. I did it this way to encapsulate the specific parts of file manipulation. It makes it a lot easier for me to read and ideally the rest of the world. It does result in more classes but I am sacrificing that for readability and maintainability. This is a technique I picked up in a HeadFirst Java Patterns book (though theirs is a bit different because of the interface set up they use which I can't change due to the restrictions of the test). Anyway, each command class has its own instance of a RandomAccessFile. So far, it works. However, since I do not have a great deal of experience with threading, I worry that these instances could potentially create problems under a heavy load.

I am trying to move all my command methods into one class - DatabaseAccess (and get rid of my CommandClasses altogether), so that all these methods all call the same reference variable pointing to my RandomAccessFile object. However, when I do this or anything like it I get some strange results.


When I call the read method from a Scanner using this instance of raf, I am prompted to enter a record number to return. The first record number I enter works fine and a record is returned, and I then enter another number (since I am in the Scanner loop I created - pretty standard). However!!! When I enter the second file to be returned this is the message that I receive:

java.io.IOException: Stream Closed
Castle * Smallville * 6 * Y * $220.00 * 2005/11/19 * * (The second record call manages to print out);
at java.io.RandomAccessFile.read(Native Method)
at java.io.RandomAccessFile.readInt(RandomAccessFile.java:771)
//local stack trace stuff

This problem goes away completely if I take the creation of the RandomAccessFile out of the constructor, and place it in the method call like this:


So I don't get it. And if I do this the problem just returns.



Losing my mind...

Thanks for your help.
Matt
Roel De Nijs
Bartender

Joined: Jul 19, 2004
Posts: 5266
    
  13

I'm not a huge fan of these commands, because they add a lot of complexity to your Data class which is not really needed. But if you are happy with it, that's fine by me

Getting a "stream closed" IOException sounds to me you are calling close() on the RAF and then trying to read from it again.

There is a clear difference between following code snippets, 1 you should have spotted yourself (because that's just about plain Java, has nothing to do with threads or RMI):

In snippet A you will always create a RAF, so if you invoke read-method 2x you will have created 2 RAF-objects. In snippet B you will only create a new RAF when no RAF was created before. So if you invoke read-method 2x you will only have created 1 RAF-object and if you call close() on this RAF-instance, you can't use it anymore...

Not being able to spot this difference, my advice would be: put this assignment aside and go out for a
Matt Pavlovich
Ranch Hand

Joined: Aug 14, 2010
Posts: 98
Hey Roel,

I actually understand those differences. My problem with understanding the problem was that if I had that one and only instance of RandomAccesFille, why my code was not able to use it. However, it is now very clear with the close(). That fact that it cannot be reopened with another call to the method that contains it was not something I was aware of. But I am now.

You rock. Thanks.
Matt

 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: RMI and Threading