Thread Template

2019-08-11 20:52发布

问题:

OK so I have a GUI Java application that permits the user to select and launch between 5-500 software devices each represented by a Thread. The Threads continuously update the GUI with information.

The user can select threads and pause, resume or kill them.

All the information on threads and thread pools, interrupts, Future.... I just got lost and wanted to set fire to the internet =)

What I need is some clear guidance on the standard way forward for this. I have below the outline of my thread class here.

Is this a good template to start with ? If not please edit.

package ACsimForm;

import java.util.Random;
import javax.swing.SwingUtilities;

public class Squeak implements Runnable {

    private  String name = "";  
    private  javax.swing.JTextArea ScroolPage;


        Squeak (String name, javax.swing.JTextArea MW )
        {
            this.value = true;
            this.name = name;
            this.ScroolPage = MW;
        }


         Random r = new Random();
         int num = r.nextInt(10-1) + 1;


    @Override
   public void run ()
        {                                 
            updateGUI("Thread "+name+" Loaded and Running");

            while(true)
            {
                updateGUI("New Data");

                try {                        
                      TimeUnit.SECONDS.sleep(10);
                } catch (InterruptedException e) {
                    updateGUI("Thread "+name+" Exiting!");
                    //return exits the method killing the thread
                    return;
                }                
            }                               
        }



 //this is the new way to communicate back to the GUI 
 private void updateGUI(final String foo) {
 SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
          ScroolPage.append(foo+"\r\n");      
        }
      });
    }
    }

What is the best way to keep tack of up 500 threads in a way that permits you to kill or pause/resume them from the GUI?

Many Thanks.

回答1:

I think your Runnable is almost there. Abandon the idea of a magic Boolean flag - this is plagued with issues (read up on volatile). Use the interrupt itself as a signal. This is why it's there!

Here is a simple example of a system with an arbitrary number of Threads:

private static final class Printer implements Runnable {

    private final String printMe;

    public Printer(String printMe) {
        this.printMe = printMe;
    }

    @Override
    public void run() {
        while (true) {
            System.out.println(Thread.currentThread().getName() + "says: " + printMe);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException ex) {
                return;
            }
        }
    }
}

public static void main(String[] args) throws Exception {
    final ExecutorService executorService = Executors.newCachedThreadPool();
    final Map<Integer, Future<?>> futures = new HashMap<>();
    for (int i = 0; i < 10; ++i) {
        futures.put(i, executorService.submit(new Printer("Printer" + i)));
    }
    final Scanner scanner = new Scanner(System.in);
    while (true) {
        final String input = scanner.nextLine();
        if ("EXIT".equalsIgnoreCase(input)) {
            break;
        }
        final Integer threadToStop;
        try {
            threadToStop = Integer.parseInt(input);
        } catch (NumberFormatException ex) {
            System.out.println("Not a number");
            continue;
        }
        final Future<?> f = futures.remove(threadToStop);
        if (f == null) {
            System.out.println("Not a valid thread");
            continue;
        }
        f.cancel(true);
    }
    executorService.shutdownNow();
}

We use an ExecutorService to manage the Threads - you should never use Threads directly, this is again plagued with land mines.

The ExecutorService is asked to run an arbitrary number of tasks - you can see how to add tasks in the for loop.

Then ExecutorService returns a Future for each task added - these are handles to the tasks. They allow us to check if they are still going/done or have encountered an error.

We Map the Futures to task names and than allow the user to selectively kill tasks from input.

You cannot "pause" and "resume" a Thread (well you can, but you are better off not worrying about that). You simply cancel a task when you want it paused and then reissue it to restart it. The good thing is that the ExecutorService will recycle the Thread that the task was running on so you won't loose performance.

If you are issuing commands to the Map of Futures from multiple threads you would need a ConcurrentHashMap.

A common pitfall is assuming that your application will exit cleanly with the ExecutorService running. This is not the case. The ExecutorService spawns non-daemon threads and so the application cannot exit until they are all done. You have two options; the first is a bit hacky - just give the ExecutorService your own ThreadFactory and make the Threads daemon, the second is to shutdown the ExecutorService when you are done with it as I have in the example.



回答2:

This is not a complete answer but a few tips to get things done.

You should use an ExecutorService for your threads, and .submit() them. In order to register them and have them available, say by name, create a Map<String, Future<?>>.

Some pseudo code, could be improved:

@GuardedBy("this") // see JSR 305
final Map<String, Future<?>> allSqueaks
    = new HashMap<String, Future<?>>();
final ExecutorService service
    = Executors.newCachedThreadPool();

// Registering:
public synchronized void registerSqueak(final Squeak squeak)
{
    allSqueaks.put(squeak.getName(), service.submit(squeak));
}

// Cancelling:
public synchronized void cancelSqueakByName(final String name)
{
    // Note: deal with non existing name if it can happen
    final Future<?> victim = allSqueaks.remove(name);
    victim.cancel(true);
}