>> Actually, it just occurred to me that uniqueness, while not strictly >> required, should certainly be encouraged. If you added the same task to a >> ParallelTaskGroup's task sequence more than once, you'd basically get >> serialized behavior, since only one thread could run the task at a time. So >> perhaps a Group is actually appropriate. > > I think that really depends on the intent again. I agree that in many > cases the supplied Tasks would not contain any duplicates, but is > there a strong reason to actually enforce it rather than just document > the behaviour?
I think the strongest argument for Group is that allowing duplicates could result in confusing behavior. Making it a Group ensures that the intent is clear. >> Worse, TaskSequence executes sub-tasks by creating (or at least obtaining) a >> new thread per task, which isn't really necessary. The same results could be >> achieved by simply calling the synchronous version of execute() (the one >> that doesn't take a listener argument) on each task in the list. > > This was part of the cause of my confusion with these classes. I > didn't see why they needed to be Tasks themselves. It is > straightforward to create and execute new Task if and when I want to, > but I can't extract the executable portion of code from a Task once it > is created. If the parallel execution code is wrapped in a Task, then > that decision has been made for me. That's true, and perhaps it is reasonable to consider making TaskGroup a non-Task class. OTOH, if such a class is not wrapped in a Task, the calling thread will block while it waits for the sub-tasks to complete, which may not be the desired behavior. If the 90% case is that a TaskGroup is run in its own Task, then maybe it should actually be a Task. > Do you remember if it was a conscious choice for these classes to > extend Task from the outset, or do they do so because they use > TaskListener for notifications? I think they just seemed like a natural fit for a Task - TaskGroup allows a caller to create a task hierarchy in the same way that Component and Container create a UI hierarchy. The idea of using a static execute() method to launch parallel tasks is interesting: public class TaskGroup { public static void execute(Sequence<Task> tasks, ExecutorService executorService) { ... } public static void execute(Sequence<Task> tasks, TaskListener listener, ExecutorService executorService) { ... } } If I understand your suggestion correctly, the first version would basically do what TaskGroup#execute() does now. The second version would wrap a call to the first version in a Task (probably implemented as a static inner class) and basically do what TaskGroup#execute(TaskListener, ExecutorService) does now. Is that correct? If so, I could go either way. I'm not sure which would be clearer to most developers - a dedicated TaskGroup class that extends Task, or these static methods. I'm inclined to think the former, but maybe that is just personal preference. G