dcsimg
synchronizing methods in singleton class
1 posts in topic
Flat View  Flat View
TOPIC ACTIONS:
 

Posted By:   devaraj_sundaram
Posted On:   Monday, May 27, 2002 06:07 PM

If I have a singleton class containing methods which does both read and write operations to the data base. Should I synchronize methods which only read a set of rows from a table. For example if a have a method Customer[] getCustomers() { .... ResultSet rs = stm.executeQuery(); while(rs.next()) { ... } If I have various business logic objects executing on multiple threads accessing the same method, is it possible that one thread might be in the middle of the while loop and its preempted by another thread? Does threads context have a separate copy of the "rs" variable? Is there a problem I should watch our for?    More>>

If I have a singleton class containing methods which does both read and write operations to the data base.
Should I synchronize methods which only read a set of rows from a table.


For example if a have a method


Customer[] getCustomers() {

....

ResultSet rs = stm.executeQuery();

while(rs.next()) {

...

}

If I have various business logic objects executing on multiple threads accessing the same method, is it possible that one thread might be in the middle of the while loop and its preempted by another thread? Does threads context have a separate copy of the "rs" variable? Is there a problem I should watch our for?

   <<Less

Re: synchronizing methods in singleton class

Posted By:   Shawn_Alexander  
Posted On:   Wednesday, May 29, 2002 12:09 PM

Ok,




It seems you posted a couple of different questions. The easiest ones first:



>>Does threads context have a separate copy of the "rs" variable? Is there a problem I should watch our for?



Yes (at least in the code you provided). The ResultSet rs declaration is on a method level. The rs object is not static, therefore each thread will have its own instance of the resultset.



>>...is it possible that one thread might be in the middle of the while loop and its preempted by another thread?



Yes. This is possible because the JVM does not guarantee the order that the threads process commands (in other words, due to limitations in memory and processor capacity, one thread may be paused and another may continue processing.



>>Should I synchronize methods which only read a set of rows from a table.





This is a much more complicated scenario. The answer to this question will vary from developer to developer.



Why synchronize?



Synchronization is important with database activity to ensure that there will not be a "dirty read" or "lost update".



When two threads attempt to perform the same operation at the same time, errors can occur within the system. For example, a husband and wife share a checking account at their bank. The code the bank uses to verify sufficient funds exist for a withdrawal may look like:



withdrawFunds(int amount)

1. {

2. int balance = getCurrentBalance()

3.

4. int newBalance = balance - amount

5.

6. //ensure that the withdrawal isn't more than the account. No overdrafts are allowed

7. if (newBalance > 0)

8. {

9. subtractMoney(amount)

10. }

11. }





The account the wife and husband share has a beginning balance of $1,000. The wife withdraws $700 at virtually the same time that the husband withdraws $800. The expected result is that one of withdrawals should not occur, because it would cause the balance of the account to fall below zero.



Suppose the husbands thread executes in this manner:



int balance = getCurrentBalance() ---> returns 1,000

newBalance = 1,000 (current balance) - withdrawal amount (800) -> 200

if (newBalance is greater than zero) --> returns true, because 200 would be left in the account



At this point, the JVM may pause the husband's thread to execute logic on a different thread (for the purposes here, the JVM can only focus on one thread of execution at a time).



The wife's thread then begins execution:


int balance = getCurrentBalance() ---> returns 1,000

newBalance = 1,000 (current balance) - withdrawal amount (700) -> 300

if (newBalance is greater than zero) --> returns true, because 300 would be left in the account



The wife's thread does not pause, but finishes executing the method in the following manner:



subtractMoney(700)



At this point, only 300 dollars is left in the account after the wife's withdrawal. Now that the wife's thread has finished executing, the husband's thread is resumed from the point it was paused by the JVM:


subtractMoney(800)





The husband's withdrawal should not have occurred, because there was not enough money left in the account to finish his request. Instead, the account ended with a balance of -$500.





Is synchronization best during database activity?



Generally speaking it depends on your deployment strategy. If your application is deployed always in a single JVM, you could probably get away with synchronizing all access. However, a couple of problems may present themselves:



"Lost update"



1) Thread one reads a row

2) Thread two modifies the row

3) Thread one modifies the results of the row

4) Thread one commits its modifications



A lost update occurs during this scenario (thread 2's modifications are lost, because thread one did not re-read the database information after thread two had modified the database).



"Dirty read"



1) Thread one reads a row

2) Thread two updates the row



Thread one has a "dirty read" because the database row has changed. The information that Thread one is now working on is incorrect.



So, is it possible to synchronize across a JVM and eliminate dirty reads and lost updates --- yes. However, this is only true if only one thread can modify, read, add, or delete from the database at any point in time.



Obviously, this is inefficient and not very practical. So, other alternatives must be explored.



1) Most databases provide locking mechanisms for connections to "check out" rows so only that connection can modify the row until it is checked back in. However, this prevents the sharing of connections across objects (EJB containers especially would have trouble with just this approach).



2) Perhaps the most elegant and efficient approach is to perform some sort of pessimistic locking. Where a timestamp (last modified) column is added to every table. When a java object performs a read on the table, the timestamp is stored in memory. Once modifications are made to the object and the object must be persisted to the database, the timestamp is re-read (as part of the SQL) to guarantee that no other changes have been made to the database row. If the timestamp returned at the point of commit differs from the timestamp returned in the initial read, the java database manager should throw an exception (since the data has been modified). The application will then start the process over to include the recent changes to the database.



Steps:



1) Thread one reads a column and retrieves a timestamp (123)

2) Thread two modifies the column and sets the timestamp(456)

3) Thread one prepares to modify the column

4) Thread one re-reads the timestamp from the column (456)

5) Thread one compares the original timestamp read with the new one (123 and 456)

6) Thread one throws an exception or repeats processing from step one



If the timestamp has not been modified, the thread can commit its modifications without worrying about causing a lost update.



Dirty reads can still exist under this solution, but will be "caught" when the thread attempts to modify the row and reads a timestamp inconsistent with the timestamp it had obtained earlier in processing.



Ultimately, your choices will be narrowed down by A) how important data integrity is to the system and B) what the deployment topology will look like (one JVM or multiple JVMs).
About | Sitemap | Contact