Help with Java multithreading

Well, this is a dual purpose question. The main thing I hope to escape from this is more knowledge about multithreading. I'm a complete newbie when it comes to multithreading, and this is my first actual attempt to do anything in multiple threads. Another thing I hope to clean up is homework help, which is what I am turning into a much more complex franken project for fun and learning. In the first part of this question, I will talk in detail about my thoughts and methodology for those flows that I worked on in my homework. If I am doing something bad, I need to correct it, regardless, please let me know so that I can study. Again, I know almost nothing about multithreading.

Firstly, I am currently pursuing a course in computer science, which, to be pleasant, has homework, which uses the methods and data structures that I have already studied, and therefore not difficult. To try not to completely get rid of my skull, I try to make a simple project (create a Linked List and sorted Linked List) and turn it into a multi-threaded franken program. I would like the method of adding new elements to be in a separate stream and get the input from the queue (not absolutely necessary for an unordered list, but it will be more useful for proper ordering), and for an ordered list I want a separate stream that will patrol the list to make sure that all elements are in order (one of the methods that I DO NOT ALLOW to change is returning links to entire nodes that have completely public data). These are the two main things that I would like to create, but definitely not the only things that I could try to figure out. This means that this is just a test to learn about multithreading, so I'm not really designing this project for practicality. If you want to leave a comment about what is good programming practice for what should be streaming or not, it would be very helpful.

As a step that I took recently, an idea for which was gleaned from reading a message about StackOverflow itself, I created a master class that gave it threads so that it could stop them and clear everything at the end of the program. This is done using the someThread.interrupt () function and checking the Thread () method for this exception. However, I found a problem with this method: a loop that goes through threads and causes an interrupt () is most often not executed. Here is the code for the method:

public static void stopAllThreads() { boolean stopped = false; while( !stopped ) { System.out.println( "Stopping all threads" ); synchronized( threads ) { System.out.println( "Threads being stopped: " + threads.size() ); for( int i = 0; i < threads.size(); i++ ) { System.out.println( "Stopping: " + threads.get( i ) ); threads.get( i ).interrupt(); } threads.clear(); stopped = true; } } } 

While trying to debug it, I set the while loop to try to get the method to eventually start the for loop, but all that happens is the outputs “Stop all threads”, and then I don’t see anything else. I do not know if this code is bad or what is wrong with this code. Any help in determining how to make this work is appreciated (if you need to know more about the class, let me know. I really don't know what you need to see, but I don't want to copy and paste a few whole java files).

In addition to this problem, I also decided that when I started my program, it would reach the last line and try to stop all threads before the thread going through the number queue to add to the list finished adding everything. This is also a problem when trying to list through the toString method immediately after adding numbers to the queue, since nothing has been added yet and therefore nothing will be output to the console. Here is my main method that shows the order of things (although I think this is less important when working with threads):

 public static void main( String[] args ) { // always make sure this is the first thing created ActiveThreads bla = new ActiveThreads(); Comparator< Integer > temp = new Comparator< Integer >() { public int compare( Integer i, Integer j ) { if( i > j ) return 1; if( i < j ) return -1; return 0; } }; List< Integer > woogle = new List< Integer >( temp ); woogle.add( 1 ); woogle.add( 2 ); woogle.add( 3 ); woogle.add( 4 ); System.out.println( woogle ); ActiveThreads.stopAllThreads(); } 

Finally, here is the user thread I made that should check the queue for items, and then add all the items to the queue in my actual list:

 private class AddThread implements Runnable { public AddThread() { } public void run() { while( running ) { synchronized( addQueue ) { while( !addQueue.isEmpty() ) { System.out.println( "Going through queue" ); T temp = addQueue.poll(); if( head == null ) { head = new Node< T >(); last = head; head.data = temp; largest = temp; smallest = temp; } else { last.next = new Node< T >(); last.next.data = temp; last = last.next; if( mComparator.compare( temp, largest ) == 1 ) { largest = temp; } else if( mComparator.compare( temp, smallest ) == -1 ) { smallest = temp; } } ++size; } } System.out.println( "Pausing " + addThread ); synchronized( addThread ) { pause( 200 ); } } } private void pause( long time ) { try { addThread.wait( time ); } catch ( InterruptedException e ) { running = false; addThread.notify(); } } private boolean running = true; } 

Now, the last thing I would like to ask for is some of the main primers and guides, as well as for good multithreading. I found many textbooks, but there are some things that people don’t mention in the textbooks, and most textbooks are the only way. If you know of any good tutorials, I would appreciate links to them. If there are good coding methodologies for multithreading, that would be greatly appreciated. It’s just that any information that, in your opinion, can help me wrap my brain around this concept, and also make sure that when I implement it, I do it in such a way that it is less prone to errors and people won’t shout at me for bad practice.

EDIT: To answer the questions that matte b puts in a comment, addThread is just a Thread reference that looks at a queue that has elements in it to add to the list and puts them in the list. This is a private thread within a class containing the AddThread class (clever use of names, huh?). Threads are built in a way that was posted in another question on StackOverflow. Here is an example (as well as a code snippet showing what addThread is):

 addThread = new Thread( new AddThread(), "Thread for adding elements" ); addThread.setPriority( 6 ); addThread.start(); ActiveThreads.addThread( addThread ); 

All topics are created in the same way. As for what I supposed Thread wrote, I explained it in detail above, but I will try to make a synopsis here.

The AddThread class is an inner class that is created during the construction of the linked list class that I am trying to do. He looks at a private queue, which may or may not have elements in it, and when she does, she places these elements in a linked list. The queue is also added when the method of adding a linked list (T newElt) is called.

EDIT EDIT: In the interest of full disclosure, as well as for those who want to spend time, I am going to post two corresponding Java files here.

List.java

 public class List< T > implements Iterable< T >, Comparable< List< T > > { // you may not change this inner class! class Node< D > { D data; Node< D > next; } public List( Comparator< T > comparator ) { mComparator = comparator; head = null; last = null; size = 0; addQueue = new LinkedList< T >(); addThread = new Thread( new AddThread(), "Thread for adding elements" ); addThread.setPriority( 6 ); addThread.start(); ActiveThreads.addThread( addThread ); } public void add( T newElt ) { synchronized( addQueue ) { addQueue.add( newElt ); } } public T get( int index ) { if( index > size ) throw new IndexOutOfBoundsException(); Node< T > temp = head; for( int i = 0; i < index; i++ ) { temp = temp.next; } return temp.data; } public Node< T > lookup( T element ) { Node< T > temp = head; for( int i = 0; i < size; i++ ) { if( temp.data == element ) return temp; temp = temp.next; } throw new NoSuchElementException(); } public int size() { return size; } public boolean isEmpty() { return head == null; } public void delete( T element ) { throw new UnsupportedOperationException("You must implement this method."); } public void replace( T oldElt, T newElt ) { try { Node< T > temp = lookup( oldElt ); temp.data = newElt; } catch( NoSuchElementException e ) { throw e; } } public T getLargest() { return largest; } public T getSmallest() { return smallest; } public List< T > copy() { throw new UnsupportedOperationException("You must implement this method."); } public String toString() { StringBuffer ret = new StringBuffer(); int i = 0; for( T x : this ) { System.out.println( "Loop: " + i++ ); ret.append( x + " " ); } return ret.toString(); } public int compareTo( List< T > other ) { throw new UnsupportedOperationException("You must implement this method."); } public ListIterator< T > iterator() { return new ListIterator< T >( head ); } private class ListIterator< E > implements Iterator< E > { private ListIterator( Node< E > head ) { cur = head; } public boolean hasNext() { if( cur == null ) return false; System.out.println( "Iterator: " + cur.data ); return cur.next == null; } @SuppressWarnings("unchecked") public E next() { Node< E > temp = cur; cur = cur.next; return (E)temp.data; } public void remove() { throw new UnsupportedOperationException("You do NOT need to implement " + "this method."); } private Node< E > cur; } private class AddThread implements Runnable { public AddThread() { } public void run() { while( running ) { synchronized( addQueue ) { while( !addQueue.isEmpty() ) { System.out.println( "Going through queue" ); T temp = addQueue.poll(); if( head == null ) { head = new Node< T >(); last = head; head.data = temp; largest = temp; smallest = temp; } else { last.next = new Node< T >(); last.next.data = temp; last = last.next; if( mComparator.compare( temp, largest ) == 1 ) { largest = temp; } else if( mComparator.compare( temp, smallest ) == -1 ) { smallest = temp; } } ++size; } } System.out.println( "Pausing " + addThread ); synchronized( addThread ) { pause( 200 ); } } } private void pause( long time ) { try { addThread.wait( time ); } catch ( InterruptedException e ) { running = false; addThread.notify(); } } private volatile boolean running = true; } private Comparator< T > mComparator; private Node< T > head, last; private Thread addThread; // replace this with your own created class later private Queue< T > addQueue; private int size; private T largest, smallest; } 

ActiveThreads.java

 public class ActiveThreads { public ActiveThreads() { if( threads == null ) { threads = new ArrayList< Thread >(); } if( threadsMonitor == null ) { ThreadsMonitor monitor = new ThreadsMonitor(); Thread thread = new Thread( monitor, "Active Threads Monitor" ); thread.setPriority( 3 ); thread.start(); threadsMonitor = thread; } } public static void stopMonitoring() { synchronized( threadsMonitor ) { threadsMonitor.interrupt(); } } public static void addThread( Thread t ) { synchronized( threads ) { threads.add( t ); System.out.println( "Added thread: " + t ); } } public static void addThread( Runnable r, String s ) { Thread t = new Thread( r, s ); t.start(); addThread( t ); } public static void stopThread( Thread t ) { synchronized( threads ) { for( int i = 0; i < threads.size(); i++ ) { if( threads.get( i ) == t ) { threads.get( i ).interrupt(); threads.remove( i ); } } } } public static void stopAllThreads() { boolean stopped = false; while( stopped == false ) { System.out.println( "Stopping all threads" ); synchronized( threads ) { System.out.println( "Threads being stopped: " + threads.size() ); for( int i = 0; i < threads.size(); i++ ) { System.out.println( "Stopping: " + threads.get( i ) ); threads.get( i ).interrupt(); } threads.clear(); stopped = true; } } } private static ArrayList< Thread > threads = null; private static Thread threadsMonitor = null; private class ThreadsMonitor implements Runnable { public ThreadsMonitor() { } public void run() { while( true ) { synchronized( threads ) { for( int i = 0; i < threads.size(); i++ ) { if( !threads.get( i ).isAlive() ) { threads.get( i ).interrupt(); threads.remove( i ); } synchronized( threadsMonitor ) { try { threadsMonitor.wait( 5000 ); } catch( InterruptedException e ) { threadsMonitor.interrupted(); return; } } } } } } } } 
+6
java multithreading
source share
2 answers

A problem was discovered: the nested synchronized block that came to the wait () call in the innermost part of the block does not release the lock in ArrayList Threads.

 synchronized( threads ) { for( int i = 0; i < threads.size(); i++ ) { if( !threads.get( i ).isAlive() ) { threads.get( i ).interrupt(); threads.remove( i ); } } } synchronized( threadsMonitor ) { try { threadsMonitor.wait( 5000 ); } catch( InterruptedException e ) { threadsMonitor.interrupted(); return; } 

Whether there was a problem by eliminating synchronization with threadMonitor inside synchronization with threads resolves the problem as shown below. (Sorry for formatting issues; copy + paste - mutable beast)

  synchronized( threadsMonitor ) { try { threadsMonitor.wait( 5000 ); } catch( InterruptedException e ) { threadsMonitor.interrupted(); return; } } synchronized( threads ) { for( int i = 0; i < threads.size(); i++ ) { if( !threads.get( i ).isAlive() ) { threads.get( i ).interrupt(); threads.remove( i ); } } } } 

Thanks to Mike Q for the multi-threaded resource. I did not have time to do much with this, but I will definitely look and hopefully learn a lot. I will also ask a separate question for the problem that I am having, other than stopping the threads.

0
source share

It is difficult to see the whole picture, but some general points and even more general sentences.

When trying to debug it, I insert a while loop to try to force the method to start the for loop, but all that happens is the outputs "Stop all threads", and then I will never see again

This is probably due to the fact that another thread is sitting in a synchronized block using the same lock and prevents your code from running. The easiest way to find out is to run it in the debugger and pause / break the program if you think it might get stuck. You should be able to check the threads and check their status. Find the status BLOCKED.

while( !stopped ) in stopAllThreads redundant because it can never loop.

In AddThread you have this

 private boolean running = true; 

When using a boolean value as a stop flag (which I think you are trying to achieve) and that the stop flag is polled by one thread but set by another, then you MUST make it volatile . There is a whole area of ​​multi-threaded Java coding that deals with data visibility, and volatile is one of the tools used to ensure correctness.

Often programs work "perfectly in most cases" without the "correct" multithreading logic. But they are broken and, most likely, will break at the most inconvenient time (usually, as soon as the client grabs it)! (If you remember one thing in your answer, remember the previous sentence: D)

Use the java.util.concurrent package as much as possible. Although it is very important to understand the basics, this package has many very useful constructs developed by very smart people who will solve many common concurrency problems.

Read Java Concurrent In Practice . For the topic being described (potentially very dry), she very well explains the concepts in an accessible way with a lot of examples. It was written by the same group that worked on the java.util.concurrent package.

From my own experience, I believe that the "visibility" of data in parallel programs is the most important and least understood part of programming Java threads. If you can turn your head around, you will be well on your way. JCIP can help you with this.

Hope this helps and good luck!

EDIT: Highlight another issue in your add-on code with this construct.

  for( int i = 0; i < threads.size(); i++ ) { if( !threads.get( i ).isAlive() ) { threads.get( i ).interrupt(); threads.remove( i ); } } 

Performing a delete () inside an indexed list scan like this will not work as expected because remove () interferes with the indices of the other elements in the list (all subsequent elements are moved down one index).

+5
source share

All Articles