• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

Multiple threading issue when dealing with synchronized seek and readFully methods

 
Ranch Hand
Posts: 451
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I did experiments with the Monkhouse' approach on synchronized seek and readFully.
Here is Monkhouse's code:


I created a test with 5 , 10, 50, 100, 150 threads reading the same file.
When I use 5 threads, the objects are created with the correct information.
When I used 10, 50, 100, 150 threads, the objects information are not correct. It seems to me that the file pointer points to the wrong location.
In the worst case, I got a EOF exception in the middle.

Theoretically, this code is synchronized and there is no possibility that the file pointer points to the wrong location.
In practice, I observe EOF exception or wrong reading when I deal with a lot of threads reading the file. I think when a thread executes, the file pointer does not move to the right position. That means the threads execute at a faster speed than the speed of the file pointer moving to the correct position.

I put Thread.sleep(x) before databasefile.seek(i) and it seems to me that problem gets solved.
The more threads I test with, the longer I the thread should sleep for. For example, I make the current thread sleep for 2 ms when I use 10 threads. I make the thread sleep for 5 ms when I use 100 threads.

So, in the exam, should I consider this scenario with a large number of clients (eg 50-150) reading the same file concurrently?
 
Sheriff
Posts: 11604
178
Hibernate jQuery Eclipse IDE Spring MySQL Database AngularJS Tomcat Server Chrome Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I don't think adding a sleep of a few milliseconds is the solution for the assignment...

Maybe the code you use is not so thread-safe as you think it is. If it's just a test/program you wrote yourself (which is not part of your actual solution), you could post the code here and maybe the problem can be pointed out by one of the ranchers.
 
Helen Ma
Ranch Hand
Posts: 451
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Here is what I am doing experiment with , not the actual work:






I am sure the singleton DB.instance.getDBFile() object is synchronized.
When I remove the Thread.sleep (2) before the seek() statement, I got EOF exception or some wrong data are read.
I have a feeling that the file pointer cannot move fast enough to catch up with the thread execution. For example, the file pointer points to the EOF when a new thread wants to move the pointer back to 0 and EOF exception is thrown.
 
Roel De Nijs
Sheriff
Posts: 11604
178
Hibernate jQuery Eclipse IDE Spring MySQL Database AngularJS Tomcat Server Chrome Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
What happens if you put the synchronized-block around the for-loop instead of in the retrieveRecord-method? Do you still need the Thread.sleep?
 
Helen Ma
Ranch Hand
Posts: 451
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi, Roel. That is a good suggestion. I will try this when I get home.

By the way, I read Roberto's Data test. He reads everything in the db file only once and put the records in memory before he starts the multiple threads doing finding/updating/deleting....
Monkhouse also read all DVDs once and put them in a Hashmap first.

Then, one thread updates a particular record (DVD record for this case) at a time. There are also concurrent readings of a record at a time. Concurrent readings of a single record is not as intensive as concurrent readings of all records.

In this situation, the intensive reading (read all records) is only performed once when the server starts or the application starts in standalone mode.

But for my test, I am actually doing the intensive readings from 50, 60, 100, 150 threads. I am sure I won't do that in the exam.
 
Helen Ma
Ranch Hand
Posts: 451
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Here is what I am doing experiment with , not the actual work:

As suggested by Roel, I synchronized the whole block of code in retrieveRecords, so that one thread can read the file while other threads cannot access that block.
It works fine despite this seems slower as no concurrent reading is allowed here.
But based on Monkhouse, performance is not required in the exam.






 
Helen Ma
Ranch Hand
Posts: 451
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Here is what I am doing experiment with , not the actual work:
This is another experiment using Monkhouse's approach:

I used read lock to lock the block in retrieveRecords, so that multiple reader threads can read the block of code concurrently.
When it comes to reading a particular location in the file, it synchronized the file, so that the seek and readFully is only accessed by one thread.

This approach performs better. But in the test, when I test with a large number of threads (eg 200 threads), the test returns EOF exception or read the wrong data.
I think :
1. Why EOF exception? When a first thread reaches EOF, a second thread tries to continue reading the file for a particular record. The code is properly locked and synchronized. But problem still occurs. It may be because the file pointer of the RandomAccessFile cannot move fast from EOF to where the particular record when the CPU switches to the second thread. So, when the second thread executes, the file pointer still points to EOF.

2. Why sometimes a wrong set of data is read? The idea may be the same as above. The first thread finishes reading a record , say record 3. The CPU switches to a second thread which is about to read another record, say record 20. However, the file pointer cannot move fast from record 3 to 20. So, the second thread ends up reading record 4 which is right next to record 3.




 
Roel De Nijs
Sheriff
Posts: 11604
178
Hibernate jQuery Eclipse IDE Spring MySQL Database AngularJS Tomcat Server Chrome Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I don't think ReadWriteLock is the one you want in this scenario, because it's not actually locking your critical code (the code where you seek and read all bytes required), because you are using readLock.

In the for-loop you call a retrieveRecords-method, but in the provided code I only see a retrieveRecord-method (without the s). Is this just a typo when copy/pasting your code into this topic or do you really have 2 methods?

is your RECORD_LENGTH the length of all fields in your record? Or is it the length of all fields in your record + length of the delete flag of each record?

In FileAccess you'll have a class instance of ReentrantLock:


Finally in the retrieveRecord-method you'll have something like:
 
Helen Ma
Ranch Hand
Posts: 451
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Yes. Thanks for pointing out. The method should be retrieveRecords(i) instead of retrieveRecord(i). The RECORD_LENGTH is the number of bytes of a record, which is a constant somewhere in the class.
To my understanding synchronized (file) works the same as lock() and unlock() for retrieveRecords(i). Monkhouse book mention that we can transform synchronized block into lock/unlock methods.
So, in this case, it will become this?
We have a synchronized block enclosing a lock/unlock critical section?
Do we need both of them?

 
Roel De Nijs
Sheriff
Posts: 11604
178
Hibernate jQuery Eclipse IDE Spring MySQL Database AngularJS Tomcat Server Chrome Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
No, you don't need both, so you can remove the synchronized-block in retrieveRecords

So RECORD_LENGTH includes the bytes of the deleted flag?
 
Helen Ma
Ranch Hand
Posts: 451
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Yes. Record length is the whole lenght of a record that includes all fields as specified.
So, the code become this:

In Monkhouse's book, its algorithm for retrieving DVD is like this:


So, you mean, instead of using readLock to support concurrent reading, use this algorithm?
 
Roel De Nijs
Sheriff
Posts: 11604
178
Hibernate jQuery Eclipse IDE Spring MySQL Database AngularJS Tomcat Server Chrome Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
If you use a ReadWriteLock and you are just reading records, it will not be thread-safe if you use RandomAccessFile because to read a record you need 2 operations (seek + readFully). So these must be executed as an atomic operation, so no other thread could mess up the file pointer. That's why in Andrew's book you have the readLock().lock() and the synchronized block.

If you just have a Lock (like I suggested for this example), then you don't need the synchronized block because only 1 thread at a time can get the lock. So if you surround the seek + readFully method calls in a lock.lock() and lock.unlock() as I showed in my previous post and you just copied in your previous post (see private Record retrieveRecords(long i)) then these method calls will be performed as an atomic operation.

Seems to me you don't have a clue at all what you are doing (or what I'm suggesting) and you are just copying the Monkhouse book. So everything that's different from the approach in the Monkhouse book is beyond your understanding.
 
Helen Ma
Ranch Hand
Posts: 451
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I think talking about threading in text is more confusing than talking about it face to face.
If I don't understand thread topics much, I cannot handle the OCJP or even pass the Operating System class in my computer science class.
(I remember Operating System class deals with deadlock, starvation, mutual exclusion a lot.)
If I don't understand much about the Monkhouse's example, I won't come up with questions about this.

By anyway, I think the following code is equivalent:


 
Roel De Nijs
Sheriff
Posts: 11604
178
Hibernate jQuery Eclipse IDE Spring MySQL Database AngularJS Tomcat Server Chrome Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Helen Ma wrote:If I don't understand thread topics much, I cannot handle the OCJP or even pass the Operating System class in my computer science class.


I don't think passing OCPJP is a guarantee that you understand threading (because there are a lot of questions and maybe you had every non-theoretical threading question wrong). There is still a huge difference in understanding threading and applying it in a real application. And I assume you are a student with very little (or none) experience in development (in a professional environment). That might explain why your code snippets are such a mess: lacks formatting, doesn't compile (because of typos I assume),... Makes it a lot harder for other ranchers to understand your code (and will result in fewer valuable answers because they have to put too much effort in understanding your code).

Take a look at your last post. It's more of the same. You mention lock() and unlock() methods. But are these methods from Lock or ReadWriteLock, impossible to tell. But it makes a huge difference for your statement "I think the following code is equivalent". In the other snippet you synchronize on a variable f, but it's never declared. I assume you mean the variable file, but that's just an assumption. In both snippets you use a variable i, which makes no sense because seek-method requires a long and readFully-method a byte[].

So please ShowSomeEffort when posting sample code. It will make answering your code-related questions a whole lot easier.
 
Helen Ma
Ranch Hand
Posts: 451
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I got my Bachelor and Master in computer science. During that time, multiple threading / parallel / distributed computing have been taught a lot in my school.
As my professor said every operating system has deadlock problem. I am not sure if this is still true nowadays.

I've working for small and big companies as a software engineer for 6 years in the US.
At work, I have been dealing with multiple threading issues that cause a lot of problems in software.
I realize handling multiple threading properly is never easy.

Another issue about the post is that I feel comfortable to limit my discussion on some general issues or Monkhouse's book. I am not supposed to post too much about my own work.
I can only show part of my experiments , but not complete code. (My professor joked : if someone wants to copy your homework, just give them some wrong answers to copy!")

But anyway, I tried something like this, similar to Monkhouse's DVD example:
When I run my test with a large amount of threads, it still gives me EOF exception.
According to Monkhouse book, multiple threads can access the retrieveRecord(), but they can only access retrieveRecords(long i) method mutual exclusively in order to read the record in the file correctly.
This is a very nice design from my perspective. However, it still throws me EOF exception as the file pointer cannot point to the right record when the CPU switch from one thread to another.




 
author and jackaroo
Posts: 12200
280
Mac IntelliJ IDE Firefox Browser Oracle C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Helen Ma wrote:I think the following code is equivalent:




I see where you are trying to go with this, but you have copied my code incorrectly, and therefore missed some steps. From my book (only relevant parts shown):

And:

So I actually had a synchronized block on my mutex within the logical record lock obtained in the public method. The two forms of locking have totally different purposes, as explained in the book (and by Roel).
 
Helen Ma
Ranch Hand
Posts: 451
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi, Andrew. Thanks for your advise.
I like your book's approach :




I have been doing something similar. I found out that the file pointer cannot move fast enough to the correct location in file when the CPU switches from one thread to another as concurrent reading is being executed. It ends up reading wrong data.
Nothing wrong with your concurrency design, but it is just the file pointer cannot catch up with the a large number of threads which are doing concurrent readings.

For example, I have 200 threads doing concurrent reading, and one thread can access the file at a time
1. Thread 1 reads the last record in the file. The file pointer is at the end of file.
2. CPU switches to Thread 2. Thread2 tries to read the first record. At this moment, only Thread2 is doing reading from the file. No other threads are accessing the file due to the synchronized block. The file pointer is supposed to move to the location and read fully. But the file pointer cannot move fast enough and ends up reading the EOF.

This is my opinion explaining why I get EOF exception when dealing with large amount of threads.
I am sure in the exam, there is not that many clients trying to read the file at the same time.
 
Helen Ma
Ranch Hand
Posts: 451
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I did an experiment with a large number of threads:



Case 1:
In the Test1 class, the threads call join method. The output shows me something like this:
T0: (record1), (record2), (record3) ....where record# is the data in each record.
T1: (record1), (record2), (record3) .....
T2: (record1), (record2), (record3).....


Case 2:
Without the join method. One of the possible output is:
T7 (record1), (record2) .....
T9: (record2), (record2), (record3)....
T4: EOF Exception....

IMHO,the file pointer cannot catch up while CPU switches from T7 to T9 and T9 ends up reading an unexpected location.
The same issue may happen for T4 too.

But in the real application, I can't use join to control the order of the threads' execution.

 
Andrew Monkhouse
author and jackaroo
Posts: 12200
280
Mac IntelliJ IDE Firefox Browser Oracle C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Helen Ma wrote:I have been doing something similar. I found out that the file pointer cannot move fast enough to the correct location in file when the CPU switches from one thread to another as concurrent reading is being executed. It ends up reading wrong data.



There is a mutex wrapping the seek and readFully operations. Unless that mutex has been changed in some way (e.g. it is now a per-thread mutex) then that seek and read-fully will work as an atomic operation. Any other thread(s) will be forced to wait until the mutex becomes available before they can begin performing their own seek operation.
 
Helen Ma
Ranch Hand
Posts: 451
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi, thanks for your reply.

To be more specific, in the above code, there is a FileAccess class. This FileAccess is static:


My threads in the test class use the same instance , f to retrieve the records in the mutex.
I checked the RandomAccessFile seek method. This is a native method.
I suspect :
when 100+ threads are using the same f to retrieve records, in theory, the mutex works. Since the seek method is controlled by the hardware, my hardware is not good enough to move the file pointer to the right location on the data file and ends up with EOF exception or reading a wrong record.
I am pretty sure it has something to do with my hardware performance problem.
 
Helen Ma
Ranch Hand
Posts: 451
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I did an experiment. Instead of calling the seek and readFully, I print out the locationInFile.
I run the code in the synchronized block just like what the retrieveDvd method using 100+ threads. The locationInFile are output correctly.
This proves that my hardware moves the file pointer to a wrong location in file due to its performance issue.
 
Roel De Nijs
Sheriff
Posts: 11604
178
Hibernate jQuery Eclipse IDE Spring MySQL Database AngularJS Tomcat Server Chrome Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I tweaked my code to be able to perform similar experiments as you: I added a readAll-method to my Data class which reads all records from the database file (represented by a single RandomAccessFile instance)

1st experiment: mark readAll-method as synchronized. This results in each thread reading all records (and printing them to console). No real concurrency, because just 1 thread can enter readAll-method and will have read all records before exiting this method (and then another thread can access the method). But also no EOFException or any other exception.
Output example:
...
T8: Pandemonium Lendmarch 6 Y$230.00 2004/05/02
T8: Castle Lendmarch 4 N$140.00 2003/09/17
T8: Grandview Lendmarch 4 N$170.00 2003/11/12
T8 has finished!
T3: Palace Smallville 2 Y$150.00 2005/07/27
T3: Castle Smallville 6 Y$220.00 2005/11/19
T3: Excelsior Smallville 4 Y$230.00 2003/02/05
...


2nd experiment: put the critical code (seek + readFully calls) in a synchronized-block using the RandomAccessFile instance as a mutex. This results in real concurrency happening: each thread reads 1 (or just a few) records from the file (and printing them to console) and then another thread can read some records until every thread has read all records. Again no EOFException or any other exception.
Output example:
...
T8: Castle EmeraldCity 4 Y$150.00 2003/05/15
T8: Grandview Xanadu 6 N$150.00 2003/08/02
T10: Bed & Breakfast & Business Whoville 2 Y$100.00 2005/08/07
T10: Grandview Whoville 6 Y$210.00 2003/06/10
T10: Excelsior Metropolis 6 Y$140.00 2005/02/28
T2: Splendide Paravel 4 N$110.00 2004/03/14
T2: Castle Lendmarch 4 N$140.00 2003/09/17
T2: Grandview Lendmarch 4 N$170.00 2003/11/12
T2 has finished!
T4: Splendide EmeraldCity 6 N$120.00 2003/06/10
T4: Elephant Inn EmeraldCity 6 Y$110.00 2003/07/28
T4: Splendide Bali Hai 6 Y$100.00 2005/04/06
...


3rd experiment: using a ReentrantLock to guard the critical code (instead of the synchronized-block). Exactly the same behavior (and similar output) as 2nd experiment (which makes sense because it has same basic behavior and semantics)


I was able to run all these experiments with 10, 50, 100, 500 and even a 1000 threads although my computer is also not state-of-the-art (WinXP, CPU 1.83Ghz, 2GB RAM). With 1000 threads the test took 117 seconds to complete with printing each record to the console, without the printing the test finished in just 1.3 seconds.


My test case:


The code of readAll-method:


The (relevant) code of readRecord-method:


Hope it helps!
 
Don't get me started about those stupid light bulbs.
reply
    Bookmark Topic Watch Topic
  • New Topic