187

If I have a util class with static methods that will call Hibernate functions to accomplish basic data access. I am wondering if making the method synchronized is the right approach to ensure thread-safety.

I want this to prevent access of info to the same DB instance. However, I'm now sure if the following code are preventing getObjectById being called for all Classes when it is called by a particular class.

public class Utils {
     public static synchronized Object getObjectById (Class objclass, Long id) {
           // call hibernate class
         Session session = new Configuration().configure().buildSessionFactory().openSession();
         Object obj = session.load(objclass, id);
         session.close();
         return obj;
     }

     // other static methods
}
Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
tomato
  • 5,644
  • 13
  • 43
  • 48

8 Answers8

243

To address the question more generally...

Keep in mind that using synchronized on methods is really just shorthand (assume class is SomeClass):

synchronized static void foo() {
    ...
}

is the same as

static void foo() {
    synchronized(SomeClass.class) {
        ...
    }
}

and

synchronized void foo() {
    ...
}

is the same as

void foo() {
    synchronized(this) {
        ...
    }
}

You can use any object as the lock. If you want to lock subsets of static methods, you can

class SomeClass {
    private static final Object LOCK_1 = new Object() {};
    private static final Object LOCK_2 = new Object() {};
    static void foo() {
        synchronized(LOCK_1) {...}
    }
    static void fee() {
        synchronized(LOCK_1) {...}
    }
    static void fie() {
        synchronized(LOCK_2) {...}
    }
    static void fo() {
        synchronized(LOCK_2) {...}
    }
}

(for non-static methods, you would want to make the locks be non-static fields)

Scott Stanchfield
  • 29,742
  • 9
  • 47
  • 65
  • 11
    Those top 4 code blocks are gold. Exactly what I was looking for. Thank you. – Ryan Shillington Jun 26 '14 at 17:08
  • Is it correct that if I use a static Lock on non-static method, no two objects of the class SomeClass will be able to run the block at the same time? – Samuel Apr 30 '15 at 07:20
  • 2
    @Samuel - Almost... It's more about threads than object instances. You're correct in that the separate instances of SomeClass will all use the same lock/monitor: the one associated with the Someclass.class object. So if two different threads were processing two different instances of SomeClass, they both couldn't run at the same time. However, if a single thread called a method in one instance of SomeClass, and that method called a method in the other instance, no blocking would happen. – Scott Stanchfield Apr 30 '15 at 21:35
  • @ScottStanchfield You have listed ways for synchronizing methods, are they all equivalent ? – Bionix1441 Mar 16 '17 at 08:03
  • 1
    @Bionix1441 - It's all about scoping. Each mechanism above gives you finer control of the locking. First, using the instance itself locking an entire method, then the instance itself to lock sections inside a method, then any object instance to lock sections. – Scott Stanchfield Mar 30 '17 at 18:24
137

By using synchronized on a static method lock you will synchronize the class methods and attributes ( as opposed to instance methods and attributes )

So your assumption is correct.

I am wondering if making the method synchronized is the right approach to ensure thread-safety.

Not really. You should let your RDBMS do that work instead. They are good at this kind of stuff.

The only thing you will get by synchronizing the access to the database is to make your application terribly slow. Further more, in the code you posted you're building a Session Factory each time, that way, your application will spend more time accessing the DB than performing the actual job.

Imagine the following scenario:

Client A and B attempt to insert different information into record X of table T.

With your approach the only thing you're getting is to make sure one is called after the other, when this would happen anyway in the DB, because the RDBMS will prevent them from inserting half information from A and half from B at the same time. The result will be the same but only 5 times ( or more ) slower.

Probably it could be better to take a look at the "Transactions and Concurrency" chapter in the Hibernate documentation. Most of the times the problems you're trying to solve, have been solved already and a much better way.

Community
  • 1
  • 1
OscarRyz
  • 196,001
  • 113
  • 385
  • 569
  • 1
    Very helpful answer!THANKS! So Hibernate takes care of cnocurrency by "optimistic locking". Then there is no need to use "synchronized" methods at all for resolve any data-access concurrecy?? Use "synchronized" methods only if the data are not stored in the database?? ..when DO you use them?? – tomato Feb 24 '09 at 00:32
  • 1
    1) I think there are some means to use pessimistic locking too. 2) Nope, the RDBMS can do that work. 3) If the data is accessed by multiple threads at the same time. 4) synchronization is useful when two thread have to share data. If they don't need to, then much better! – OscarRyz Feb 24 '09 at 00:42
  • 7
    Any fast food restaurant uses multithread. One thread takes you order and use another thread to prepare it, and continues with the next customer. The synchronization point works only when they interchange information to know what to prepare. Following a model like that really simplifies life. – OscarRyz Feb 24 '09 at 00:48
  • 5
    "the whole class" is **not** locked. The [Java machine language specification](http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.3.6): `For a class (static) method, the monitor associated with the Class object for the method's class is used. For an instance method, the monitor associated with this (the object for which the method was invoked) is used.` Thus if one thread enters a static method, the same **object** returned by [Object#getClass](http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#getClass()) is locked. Other threads can still access instance methods. – Martin Andersson Apr 04 '13 at 17:05
  • @MartinAndersson That's correct, the whole class level artifacts (methods and attributes) are locked while the instance level artifacts are not. I'll update the wording – OscarRyz Apr 05 '13 at 19:11
  • 4
    lol I find that my own wording aren't that ultimately correct either. I said "Thus if one thread enters a static method, the same object returned by Object#getClass is locked". Not technically correct. Long story made short for all curious people: For each class in your application, there exist a `Class` object, instantiated by one of the virtual machines classloaders. Like all objects, this object too has a `Monitor` associated with it. And this **monitor** is what is being locked. – Martin Andersson Apr 06 '13 at 09:11
18

Static methods use the class as the object for locking, which is Utils.class for your example. So yes, it is OK.

starblue
  • 55,348
  • 14
  • 97
  • 151
15

static synchronized means holding lock on the the class's Class object where as synchronized means holding lock on the class' instance. That means, if you are accessing a non-static synchronized method in a thread (of execution) you still can access a static synchronized method using another thread.

So, accessing two same kind of methods(either two static or two non-static methods) at any point of time by more than a thread is not possible.

Scott Stanchfield
  • 29,742
  • 9
  • 47
  • 65
prasad
  • 151
  • 1
  • 3
10

Why do you want to enforce that only a single thread can access the DB at any one time?

It is the job of the database driver to implement any necessary locking, assuming a Connection is only used by one thread at a time!

Most likely, your database is perfectly capable of handling multiple, parallel access

oxbow_lakes
  • 133,303
  • 56
  • 317
  • 449
  • I am betting it is/was a workaround for some transactional issue. I.e., the solution doesn't solve the true problem – matt b Feb 23 '09 at 20:37
  • 1
    I didn't know that....I thought I'd have to manually implement this. Thanks for pointing it out! :) – tomato Feb 24 '09 at 00:36
2

If it is something to do with the data in your database, why not utilize database isolation locking to achieve?

Ray Lu
  • 26,208
  • 12
  • 60
  • 59
2

To answer your question, yes it does: your synchronized method cannot be executed by more than one thread at a time.

David Z
  • 128,184
  • 27
  • 255
  • 279
2

How the synchronized Java keyword works

When you add the synchronized keyword to a static method, the method can only be called by a single thread at a time.

In your case, every method call will:

  • create a new SessionFactory
  • create a new Session
  • fetch the entity
  • return the entity back to the caller

However, these were your requirements:

  • I want this to prevent access to info to the same DB instance.
  • preventing getObjectById being called for all classes when it is called by a particular class

So, even if the getObjectById method is thread-safe, the implementation is wrong.

SessionFactory best practices

The SessionFactory is thread-safe, and it's a very expensive object to create as it needs to parse the entity classes and build the internal entity metamodel representation.

So, you shouldn't create the SessionFactory on every getObjectById method call.

Instead, you should create a singleton instance for it.

private static final SessionFactory sessionFactory = new Configuration()
    .configure()
    .buildSessionFactory();

The Session should always be closed

You didn't close the Session in a finally block, and this can leak database resources if an exception is thrown when loading the entity.

According to the Session.load method JavaDoc might throw a HibernateException if the entity cannot be found in the database.

You should not use this method to determine if an instance exists (use get() instead). Use this only to retrieve an instance that you assume exists, where non-existence would be an actual error.

That's why you need to use a finally block to close the Session, like this:

public static synchronized Object getObjectById (Class objclass, Long id) {    
     Session session = null;
     try {
         session = sessionFactory.openSession();
         return session.load(objclass, id);
     } finally {
         if(session != null) {
             session.close(); 
         }
     }
 }

Preventing multi-thread access

In your case, you wanted to make sure only one thread gets access to that particular entity.

But the synchronized keyword only prevents two threads from calling the getObjectById concurrently. If the two threads call this method one after the other, you will still have two threads using this entity.

So, if you want to lock a given database object so no other thread can modify it, then you need to use database locks.

The synchronized keyword only works in a single JVM. If you have multiple web nodes, this will not prevent multi-thread access across multiple JVMs.

What you need to do is use LockModeType.PESSIMISTIC_READ or LockModeType.PESSIMISTIC_WRITE while applying the changes to the DB, like this:

Session session = null;
EntityTransaction tx = null;

try {
    session = sessionFactory.openSession();
    
    tx = session.getTransaction();
    tx.begin();

    Post post = session.find(
        Post.class, 
        id, 
        LockModeType.LockModeType.PESSIMISTIC_READ
    );

    post.setTitle("High-Performance Java Perisstence");

    tx.commit();
} catch(Exception e) {
    LOGGER.error("Post entity could not be changed", e);
    if(tx != null) {
        tx.rollback(); 
    }
} finally {
    if(session != null) {
        session.close(); 
    }
}

So, this is what I did:

  • I created a new EntityTransaction and started a new database transaction
  • I loaded the Post entity while holding a lock on the associated database record
  • I changed the Post entity and committed the transaction
  • In the case of an Exception being thrown, I rolled back the transaction
Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911