Resource sharing among threads, different behavior in different versions of Java

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

  • Multiple threads (inner classes that implement Runnable) sharing the data structure (instance variable of the upper class).

  • Work: took classes from the bin folder of the Eclipse project project, is launched on a Unix machine.

  • DOES NOT WORK: directly compiled src on a Unix machine and used these class files. The code compiles and then runs without errors / warnings, but one thread cannot normally access the shared resource.

  • PROBLEM: One thread adds items to the above shared DS. The second thread does the following ...

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

    }

  • The log shows that the size is updated in stream 1.

  • For some mystical reason, the workflow does not start if () ...

The same exact code works fine if I directly paste class files from the Eclipse bin folder.

I apologize if I missed something obvious.

code:

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

// Topic 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 Many thanks @maasg ...

The solution was to add synchronized (myArrayList) before checking the size in stream 2.

+4
source share
4 answers

To access the general structure in a multi-threaded environment, you must use implicit or explicit locking to ensure secure publishing and access to streams. 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 is similar to producer-consumer and is better implemented using a queue. LinkedBlockingQueue is a good option for this and provides built-in concurrency management capabilities. This is a good structure for safely publishing data between threads. Using a parallel data structure allows you to get rid of a synchronized block:

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

Each time you modify a given shared variable within a parallel region (an area with multiple threads running in parallel), you must ensure mutual exclusion . You can guarantee mutual exclusion in Java with synchronized or locks , usually you use locks if you want finer grain synchronization.

If program-only performance is read on a given shared variable, there is no need to synchronize access to this variable.

Since you are new to this topic, I recommend you this tutorial

+1
source

Check return

 newCSRequests.add((CSRequest) message); 

I suppose this is possible because for some reason it was not added. If it was a HashSet or similar, it could be because the hash code for multiple objects returns the same value. What is the equivalent implementation of a message object?

You can also use

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

to ensure proper synchronization of the arraylist.

NTN

0
source

If I get it right. There are at least 2 threads that work with the same general data structure. The array you mentioned. One thread adds values ​​to the array, and the second thread "makes stuff" if the size of the array is> 0. There is a chance that the thread scheduler executed the second thread (which checks if the collection is> 0) before the first thread gets a chance to start and add value. Running classes from bin or recompiling them has nothing to do. If you run the application again from the bin directory, you may see this problem again. How many times have you run the application? It may not play back sequentially, but at some point you may see the problem again.

You can access datastruce in a serial fashion, allowing only one thread to access the array at a time. However, this does not guarantee that the first thread will be started, and only then the second one checks if it has size> 0.

Depending on what you need to accomplish, there may be better / other ways to achieve this. It is not necessary to use an array to coordinate threads.

0
source

Source: https://habr.com/ru/post/1442251/


All Articles