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; } } } } } } } }