简体   繁体   中英

How to best synchronize two threads accessing one class in Java

I have a program that sends SNMP commands and listens for traps.

My first method coordinates the sending of SNMP. Once he's done sending, I need him to wait until he receives a notification that traps have been received, so he can continue to run. Right now, I'm trying to do that via synchronizing a global object, but it's not working and I'm not sure it's the ideal solution for me.

public synchronized int loginUser(ScopedPDU pdu) throws IOException
{

    loginUserManager = new LoginUserManager();

    pdu = buildPdu();   

// send command
        errorStatus = snmpManager.snmpSend(pdu);

        if (errorStatus == SnmpManager.SNMP_PASS)
        {
            // Wait for traps
            synchronized(syncObject)
            {
// read global variable status to see if it's no longer in progress
                while(status == TrapStatus.INPROGRESS)
                {
                    try {
                        System.out.println("Waiting for login to be done");
                        syncObject.wait();
                        System.out.println("We received a notify");
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        } //(errorStatus == SnmpManager.SNMP_PASS)
        else
        {
            return TrapStatus.FAIL;
        }

    System.out.println("Returning status");
    return status;

}

My second method is receiving a trap (and runs on a different thread that the first method) and getting the correct status I want, but I'm not able to notify the other thread.

public synchronized void readTrap(Vector v)
{
    Enumeration e = v.elements();

    if(v.contains(PduTrap.UserLoginStatusTrap))
    {
// gets status of login
        status = loginUserManager.getStatus(e);

        synchronized(syncObject)
        {
// notify first method to read the new status
            syncObject.notify();            
        }
    }
        }

status and SyncObject are global types that I'm trying to share between the threads. I initialize status to INPROGRESS in my class constructor.

private int status;
private Object syncObject;  

Could someone please tell me why things aren't working, or if I'm totally going about this the wrong way? Currently, my loginUser method isn't being notified by readTrap. Thank you

You might consider a shared BlockingQueue so the readTrap(...) method would add() to the queue (maybe the new status?) and the loginUser(...) would take() from the queue. This takes care of all of the locking and signaling without you having to do the wait/notify.

In terms of debugging your code, you need to make sure that the syncObject is a private final object so the same object reference is shared by both the wait and notify. Obviously if they are working from different objects then your code won't work.

Also, you should set the status inside the synchronized block. I'm not sure it makes a difference here but any values that are critical to the code should be in the mutex block. You might consider printing the value of status in both methods to make sure it is being updated like you think.

This isn't the only way to do this sort of thing but it is common for people to think of this kind of flow as the solution. (I hope this isn't so abstract that it doesn't make sense.)

--Method 1--

  1. Do part of something
  2. Signal something else to do some work asynchronously
  3. Do nothing until some result happens
  4. Finish doing what you started
  5. Return the completion status to some other code.

It can be done that way but is often a lot harder than another way where you split it into two sets of steps.

--Method 2--

Part A

  1. Do part of something
  2. Save away the the work in progress
  3. Signal something else to do some work asynchronously
  4. Forget about it

Part B

  1. Get notified that the other thing is done.
  2. Retrieve the work in progress
  3. Do the rest of the work
  4. All done

Notice the one key difference is that, the first way, you actually have result status you can return to the code that calls this stuff. The second way, the two parts are no longer synchronous and you can't do that.

What you want to do depends on what the caller needs.

Maybe there is a list of SNMP commands that need doing. If so, all you need is for Part B to mark things off the list when the are done or mark failure so someone else can retry or notify some human.

Maybe there is an ordering to the SNMP commands and you have to do one and then the other in order. The 1st method works well for that as you can put the calls in sequence. The 2nd method makes that harder since the asynchronous completion of Part B will have to look up and then kick off the next item in the sequence. That becomes complicated.

Please make your syncObject Object, as final (private Object syncObject = new Object();); please note that synchronized block uses the Object reference; so in case if the Object reference get changed, you will not see the needed behavior.

Also, please remove synchronized from both the function, why you need it? If both the thread are working on same Object; it may cause issue. Please note that synchronized method use this reference for synchronization.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM