简体   繁体   中英

Sharing a resource among Threads, different behavior in different java versions

This is the first time I've encountered something like below.

  • Multiple Threads (Inner classes implementing Runnable) sharing a Data Structure (instance variable of the upper class).

  • Working: took classes from Eclipse project's bin folder, ran on a Unix machine.

  • NOT WORKING: directly compiled the src on Unix machine and used those class files. Code compiles and then runs with no errors/warnings, but one thread is not able to access shared resource properly.

  • PROBLEM: One thread adds elements to the above common DS. Second thread does the following...

     while(true){ if(myArrayList.size() > 0){ //do stuff }

    }

  • The Log shows that the size is updated in Thread 1.

  • For some mystic reason, the workflow is not enetering if() ...

Same exact code runs perfectly if I directly paste the class files from Eclipse's bin folder.

I apologize if I missed anything obvious.

Code:

ArrayList<CSRequest> newCSRequests = new ArrayList<CSRequest>();

//Thread 1

private class ListeningSocketThread implements Runnable {
    ServerSocket listeningSocket;

    public void run() {
        try {
            LogUtil.log("Initiating...");
            init(); // creates socket
            processIncomongMessages();
            listeningSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void processIncomongMessages() throws IOException {     
        while (true) {
            try {
                processMessage(listeningSocket.accept());
            } catch (ClassNotFoundException e) {                    
                e.printStackTrace();
            }
        }
    }

    private void processMessage(Socket s) throws IOException, ClassNotFoundException {
        // read message
        ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
        Object message = ois.readObject();
        LogUtil.log("adding...: before size: " + newCSRequests.size());
        synchronized (newCSRequests) {
                newCSRequests.add((CSRequest) message);
        }
        LogUtil.log("adding...: after size: " + newCSRequests.size()); // YES, THE SIZE IS UPDATED TO > 0
        //closing....
        
    }
    
........

}

//Thread 2
private class CSRequestResponder implements Runnable {

        public void run() {
            LogUtil.log("Initiating..."); // REACHES..
            while (true) {
//              LogUtil.log("inside while..."); // IF NOT COMMENTED, FLOODS THE CONSOLE WITH THIS MSG...
                if (newCSRequests.size() > 0) { // DOES NOT PASS
                    LogUtil.log("inside if size > 0..."); // NEVER REACHES....
                    try {
                        handleNewCSRequests();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
....
}

UPDATE

Solution was to add synchronized(myArrayList) before I check the size in the Thread 2.

To access a shared structure in a multi-threaded environment, you should use implicit or explicit locking to ensure safe publication and access among threads. Using the code above, it should look like this:

while(true){
    synchronized (myArrayList) {
        if(myArrayList.size() > 0){
            //do stuff
        }
    }
    //sleep(...) // outside the lock!
}

Note: This pattern looks much like a producer-consumer and is better implemented using a queue. LinkedBlockingQueue is a good option for that and provides built-in concurrency control capabilities. It's a good structure for safe publishing of data among threads. Using a concurrent data structure lets you get rid of the synchronized block:

Queue queue = new LinkedBlockingQueue(...)
...
while(true){
        Data data = queue.take(); // this will wait until there's data in the queue
        doStuff(data);
}

Every time you modify a given shared variable inside a parallel region (a region with multiple threads running in parallel) you must ensure mutual exclusion . You can guarantee mutual exclusion in Java by using synchronized or locks , normally you use locks when you want a finer grain synchronization.

If the program only performance reads on a given shared variable there is no need for synchronized/lock the accesses to this variable.

Since you are new in this subject I recommend you this tutorial

Check the return of

newCSRequests.add((CSRequest) message);

I am guessing its possible that it didn't get added for some reason. If it was a HashSet or similar, it could have been because the hashcode for multiple objects return the same value. What is the equals implementation of the message object?

You could also use

List list = Collections.synchronizedList(new ArrayList(...));

to ensure the arraylist is always synchronised correctly.

HTH

If I got this right.. There are at least 2 threads that work with the same, shared, datastructure. The array you mentioned.. One thread adds values to the array and the second thread "does stuff" if the size of the array > 0. There is a chance that the thread scheduler ran the second thread (that checks if the collection is > 0), before the first thread got a chance to run and add a value. Running the classes from bin or recompiling them has nothing to do. If you were to run the application over again from the bin directory, you might seen the issue again. How many times did you ran the app? It might not reproduce consistently but at one point you might see the issue again.

You could access the datastruce in a serial fashion, allowing only one thread at a time to access the array. Still that does not guarantee that the first thread will run and only then the second one will check if the size > 0.

Depending on what you need to accomplish, there might be better / other ways to achieve that. Not necessarily using a array to coordinate the threads..

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