2

I have a method:

private synchronized Long generateID (Short company) throws Exception {
    IDDAO iDDAO = SpringApplicationContext.getBean (IDDAO.class);
    ID iD = iDDAO.findByNaturalKey (new IDNatKey (company);

    if (iD != null) {
        // Check if ID has reached limit, then reset the ID to the first ID
        if (iD.getLatestIDno ().longValue () == iD.getLastIDno ().longValue ()) {
            iD.setLatestIDno (iD.getFrstIDno ());
        }

        // Get next ID
        iD.setLatestIDno (iD.getLatestIDno () + 1);
        // update database with latest id
        iDDAO.update (iD);

        return iD.getLatestIDno ();
    }
}

In this code, I am updating the value of ID iD.setLatestIDno(iD.getLatestIDno() + 1). This is done in synchronized manner so that it never duplicates when accessed from multiple threads.

My question is if making this method synchronized will prevent other threads from accessing it? Or will other threads can access it from different objects? So shall it be made static?

The code is used like this

Long check = generateID (123);

Thanks

Aditya W
  • 652
  • 8
  • 20
Aiden
  • 460
  • 2
  • 11
  • 29
  • Can someone please show me how to use incrementAndGet() from AtomicLong by editing my code ? – Aiden Aug 12 '16 at 14:27

3 Answers3

3

Other threads can accessed it from an other instance:

public synchronized Long generateID(Short company) {
    // do something
}

Is equivalent to:

public Long generateID(Short company) {
    synchronized(this) {
        // do something
    }
}

So if this refers to a different instance, the thread will not block.

If you want to synchronize threads over different instances, you need to provide a common lock:

Object lock = new Object();
MyClass c1 = new MyClass(lock);
MyClass c2 = new MyClass(lock);
// ...

// in MyClass:
private Long generateID(Short company) {
    synchronized(lock) {
        // do something
    }
}

You can also a use Lock instead of using synchronized on an object. The logic remains similar though.

Since you are using SpringApplicationContext, you can create a ReentrantLock and add it to your context and then access it the same you are accessing the IDDAO instance.

Jean Logeart
  • 52,687
  • 11
  • 83
  • 118
  • It's a private method, and so it looks like it will only be accessed by `this` as original written, and will never be accessed by other objects. – Hovercraft Full Of Eels Aug 12 '16 at 13:53
  • Is there a way to LOCK whole method for everyone, all instances without using common lock ? – Aiden Aug 12 '16 at 13:54
  • I have to wonder if the OP is asking an [XY Problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) in disguise and have to wonder what the motive is behind the question. – Hovercraft Full Of Eels Aug 12 '16 at 13:54
  • 1
    @Aiden: back up please -- what **overall** problem are you trying to solve? Please give the pertinent background information. – Hovercraft Full Of Eels Aug 12 '16 at 13:55
  • @HovercraftFullOfEels It is called from some public method anyway. Like: ``public updateComnpany(short companyId) { Lond id = generateID(companyId); ... }`` – Jean Logeart Aug 12 '16 at 13:56
  • Actually I have a java service, that calls this Class ClassGenerateID which has this method inside it. The application is deployed on server. I am wondering if two different users access the class / code at same time, it will generate duplicate ID. So, I want to lock it for all users using application – Aiden Aug 12 '16 at 13:56
  • The private method is called from inside the Main class which is called by code when user presses button on UI – Aiden Aug 12 '16 at 13:57
  • why not just call [incrementAndGet](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicInteger.html#incrementAndGet())? – mrek Aug 12 '16 at 14:04
  • because the code has already been written and designed. If you will be kind enough to tell me how to use it ? – Aiden Aug 12 '16 at 14:08
  • if the server is calling the classes, synchronized will still not work ? – Aiden Aug 12 '16 at 14:09
  • Re, "because the code already has been written and designed." That is not a good reason to use a bad design (NOTE: I'm not saying _your_ design is bad, but in general...) We all sometimes start a project by going in the wrong direction. Sometimes the best way to get onto the right path is to go _back_, and try again. – Solomon Slow Aug 12 '16 at 14:31
  • Could we resolve this by using incrementAndGet() method from AtomicInteger class ? If yes Could you please help me edit the code I wrote with that class ? – Aiden Aug 12 '16 at 14:48
  • @Aiden I edited my answer: "Since you are using SpringApplicationContext, you can create a ReentrantLock and add it to your context and then access it the same you are accessing the IDDAO instance." – Jean Logeart Aug 12 '16 at 17:20
1

or some other threads can access it, from different objects ?

Of course they can, synchronized is only for syncing threads on specific object. I also wouldn't make it static - the easiest way to achieve what you want to do is using incrementAndGet() method from AtomicInteger (javadoc) class.

Definition:

public static AtomicInteger counter = new AtomicInteger(0); 
// starting number is 0, but can be changed

Usage:

int id = counter.incrementAndGet(); // assigns number 1
int otherId = counter.incrementAndGet(); // assigns 2

You can find a lot of examples when you google them, f.e. this one. There are many other methods AtomicInteger offers, I suggest you to look at the javadoc (see second link).

Thruth to be told, I didn't get why are you reseting IDs - but okay, let's say you need to do that. When ID gets through limit (let's say constant MAXIMUM_ID), there are many options how to do that:

  • use modulo: counter.incrementAndGet() % MAXIMUM_ID, but overflow can happen so you need to take care of that;

  • naive solution could be to just reset the counter:

    id = counter.incrementAndGet(); // too high
    if(id > MAXIMUM_ID) {
       counter = new AtomicInteger(0);
       id = counter.incrementAndGet(); // gets 1
    }   
    

Which prevents overflow. But then concurrency problem can happen - imagine two new counters are created, so both of them gets number 1. Therefore, instead of creating new AtomicInteger you just reset the value by method set(int value) (see javadoc for more info):

counter.set(0);

This approach is usually used because it should be faster and you are sure it is thread safe (also, why do something what is already done).

Community
  • 1
  • 1
mrek
  • 1,655
  • 1
  • 21
  • 34
  • Oh and I didn't notice - you are using long, so use [AtomicLong](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicLong.html) instead of AtomicInteger. Sorry for that. – mrek Aug 12 '16 at 15:09
  • If you are satisfied you can upwote. :) And I suggest you to use [camelCase](https://en.wikipedia.org/wiki/CamelCase) when writing java code. Also, if you like annotations, use [@Threadsafe](http://stackoverflow.com/questions/11362298/guardedby-threadsafe-notthreadsafe). – mrek Aug 12 '16 at 15:22
  • I just did :) Thanks – Aiden Aug 12 '16 at 15:23
0

When 2 threads are about to execute a synchronized method in a class, they only conflict if they are working over the same instance of the object. In this case, only one thread at a time will be able to execute the method. The other thread will need to wait until the first one finishes its method call.

Notice that a thread holds its locks even when it goes to sleep, so you should synchronize as few lines of code as you can.

This could be achieved by synchronizing block of code (or even a single line of code) instead of whole methods.

class MyClass{
    public void myMethod(){
        ...
        synchronized(this){
            // All code in this block is synchronized.
        }
        ....
    }
}

If you must synchronize the operation over different instances, then you should make the method static. Once done this, you can synchronize the whole method:

class MyClass{
    public static synchronized void myMethod(){
        ...            
    }
}

or a block of code inside the method:

class MyClass{
    public static void myMethod(){
        ...
        Class c1 = Class.forName("MyClass");
        synchronized(c1){
            // All code in this block is synchronized.
        }
        ....
    }
}
debus
  • 630
  • 4
  • 9
  • Can I synchronize whole method ? method is called from a main service class, which is called by framework classes. I am very confused if multiple instances of class are created but one thing I know, each service call is a new transaction, so it means multiple objects will be made. Will synchronizing method still work ? – Aiden Aug 12 '16 at 14:20
  • You can simply use `MyClass.class` instead of `Class.forName("MyClass")` to refer to your class. Then, you are not required to deal with a potential `ClassNotFoundException`, as the class is known to be in scope. – Holger Jan 27 '17 at 07:24