Home > Software design >  Java Multithreaded Program NotifyListener Behavior
Java Multithreaded Program NotifyListener Behavior

Time:09-13

I've been working on writing a simple program in an attempt to understand Java's multithreading feature in combination with Listeners to use as notifying agent at Thread's closure. Below are the Interface/classes that are in use (mostly grabbed from the web and tweaked a bit):

public interface ThreadCompleteListener {
    void notifyOfThreadComplete(final NotifyingThread thread);
}
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

public abstract class NotifyingThread extends Thread {
    private final Set<ThreadCompleteListener> listeners = new CopyOnWriteArraySet<ThreadCompleteListener>();

    public final void addListener(final ThreadCompleteListener listener) {
        listeners.add(listener);
    }

    public final void removeListener(final ThreadCompleteListener listener) {
        listeners.remove(listener);
    }

    private final void notifyListeners() {
        for (ThreadCompleteListener listener : listeners) {
            listener.notifyOfThreadComplete(this);
        }
    }

    @Override
    public final void run() {
        try {
            doRun();
        } finally {
            notifyListeners();
        }
    }
    
    
    public abstract void doRun();
}

Version 1 of Class InvokeThread

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class InvokeThread implements ThreadCompleteListener{
    List<String> synList = Collections.synchronizedList(new ArrayList<String>());
    int threadNotifyCount = 0;
    String threadList = "";
    
    public static void main(String[] args) {        
        InvokeThread ma_0 = new InvokeThread();     
        ma_0.execute(args);
    }

    @Override
    public void notifyOfThreadComplete(NotifyingThread thread) {
        String _temp = threadList.substring(0, threadList.length()-1);
        threadNotifyCount  ;
        if(threadNotifyCount == _temp.split(",").length) {
            Collections.sort(synList);
            System.out.println(thread.getName()   ": "   synList);
        }               
    }
    
    public String execute(String[] args) {
        
        // Start the Thread
        NotifyingThread thread1 = new NotifyingThread() {
            
            @Override
            public void doRun() {
                synchronized (this) {
//                  synList.addAll(Arrays.asList("zebra", "cow", "rabbit"));
                    threadList = this.getName()   ","   threadList;
                    synList.addAll(Arrays.asList(args[0].split(",")));
                }
                                
            }
        };
        
        thread1.addListener(this); 
        thread1.start();
        
        NotifyingThread thread2 = new NotifyingThread() {
            
            @Override
            public void doRun() {               
                synchronized (this) {
                    threadList = this.getName()   ","   threadList;
                    synList.addAll(Arrays.asList(args[idx].split(",")));
                }                               
            }
        };
        
        thread2.addListener(this);
        thread2.start();
    
        return "";
    }

}

Version 2 of Class InvokeThread

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class InvokeThread implements ThreadCompleteListener{
    List<String> synList = Collections.synchronizedList(new ArrayList<String>());
    int threadNotifyCount = 0;
    String threadList = "";
    
    public static void main(String[] args) {        
        InvokeThread ma_0 = new InvokeThread();     
        ma_0.execute(args);
    }

    @Override
    public void notifyOfThreadComplete(NotifyingThread thread) {
        String _temp = threadList.substring(0, threadList.length()-1);
        threadNotifyCount  ;
        if(threadNotifyCount == _temp.split(",").length) {
            Collections.sort(synList);
            System.out.println(thread.getName()   ": "   synList);
        }               
    }
    
    public String execute(String[] args) {
        
        int argsCount = args.length;
        
        for(int idx=0;idx<argsCount;idx  ) {
            NotifyingThread thread = new NotifyingThread() {
                
                @Override
                public void doRun() {               
                    synchronized (this) {
                        threadList = this.getName()   ","   threadList;
                        synList.addAll(Arrays.asList(args[1].split(",")));
                    }                               
                }
            };
            thread.addListener(this);
            thread.start();
        }   
                
        return "";
    }

}

Input arguments:

"zebra,cow,rabbit"
"giraffe,camel,buffalo"

Given above two versions of a given class InvokeThread. The first version does print a sorted list of String objects, consistently. The second version occasionally prints nothing. Please, help me understand the occurrence. TIA.

EDIT - 1 (after changing the code to fix the bug that Solomon reported on comments section):

Version 2 of NotifyingThread:

import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

public abstract class NotifyingThread extends Thread {
    private final Set<ThreadCompleteListener> listeners = new CopyOnWriteArraySet<ThreadCompleteListener>();

    public final void addListener(final ThreadCompleteListener listener) {
        listeners.add(listener);
    }

    public final void removeListener(final ThreadCompleteListener listener) {
        listeners.remove(listener);
    }

    private final void notifyListeners() {
        for (ThreadCompleteListener listener : listeners) {
            listener.notifyOfThreadComplete(this);
        }
    }
    
    private int idx;

    public NotifyingThread(int index) {
        this.idx = index;
    }

    @Override
    public final void run() {
        try {
            doRun(idx);
        } finally {
            notifyListeners();
        }
    }
    
    
    public abstract void doRun(int idx);
}

Version 2.1 of InvokeThread:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class InvokeThread implements ThreadCompleteListener{
    List<String> synList = Collections.synchronizedList(new ArrayList<String>());
    int threadNotifyCount = 0;
    String threadList = "";
    
    public static void main(String[] args) {        
        InvokeThread ma_0 = new InvokeThread();     
        ma_0.execute(args);
    }

    @Override
    public void notifyOfThreadComplete(NotifyingThread thread) {
        String _temp = threadList.substring(0, threadList.length()-1);
        threadNotifyCount  ;
        if(threadNotifyCount == _temp.split(",").length) {
            Collections.sort(synList);
            System.out.println(thread.getName()   ": "   synList);
        }               
    }
    
    public String execute(String[] args) {
        
       int argsCount = args.length;
        
        for(int idx=0;idx<argsCount;idx  ) {
            NotifyingThread thread = new NotifyingThread(idx) {
                
                @Override
                public void doRun(int idx) {                
                    synchronized (this) {
//                      synList.addAll(Arrays.asList("giraffe", "camel", "buffalo"));
                        threadList = this.getName()   ","   threadList;
                        synList.addAll(Arrays.asList(args[idx].split(",")));
                    }                               
                }
            };
            thread.addListener(this);
            thread.start();
        }           
        
        return "";
    }

}

The fixture does fix the original problem reported which was v2 of this class not printing list occasionally. However, I would like to keep the question open to request clarity on Solomon's remark on no-synchronization.

CodePudding user response:

Following changes (as a result of Solomon's prompt) offered fixture:

Version 2 of NotifyingThread:

import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

public abstract class NotifyingThread extends Thread {
    private final Set<ThreadCompleteListener> listeners = new CopyOnWriteArraySet<ThreadCompleteListener>();

    public final void addListener(final ThreadCompleteListener listener) {
        listeners.add(listener);
    }

    public final void removeListener(final ThreadCompleteListener listener) {
        listeners.remove(listener);
    }

    private final void notifyListeners() {
        for (ThreadCompleteListener listener : listeners) {
            listener.notifyOfThreadComplete(this);
        }
    }
    
    private int idx;

    public NotifyingThread(int index) {
        this.idx = index;
    }

    @Override
    public final void run() {
        try {
            doRun(idx);
        } finally {
            notifyListeners();
        }
    }
    
    
    public abstract void doRun(int idx);
}

Version 2.1 of InvokeThread:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class InvokeThread implements ThreadCompleteListener{
    List<String> synList = Collections.synchronizedList(new ArrayList<String>());
    int threadNotifyCount = 0;
    String threadList = "";
    
    public static void main(String[] args) {        
        InvokeThread ma_0 = new InvokeThread();     
        ma_0.execute(args);
    }

    @Override
    public void notifyOfThreadComplete(NotifyingThread thread) {
        String _temp = threadList.substring(0, threadList.length()-1);
        threadNotifyCount  ;
        if(threadNotifyCount == _temp.split(",").length) {
            Collections.sort(synList);
            System.out.println(thread.getName()   ": "   synList);
        }               
    }
    
    public String execute(String[] args) {
        
       int argsCount = args.length;
        
        for(int idx=0;idx<argsCount;idx  ) {
            NotifyingThread thread = new NotifyingThread(idx) {
                
                @Override
                public void doRun(int idx) {                
                    synchronized (this) {
//                      synList.addAll(Arrays.asList("giraffe", "camel", "buffalo"));
                        threadList = this.getName()   ","   threadList;
                        synList.addAll(Arrays.asList(args[idx].split(",")));
                    }                               
                }
            };
            thread.addListener(this);
            thread.start();
        }           
        
        return "";
    }

}
  • Related