A few thread questions (I have done my research, don't worry :) )
Hi :)
There are a couple of things I'd like to ask about threads. I've read an awful lot over the past couple of days, and think I mostly understand the concepts that keywords like synchronized, volatile and lock objects represent.
The code I've written is below; the size / specific contents of it aren't really important; what I'm about to ask below isn't very complicated.
privatevoid startScan()throws InterruptedException{
final Runnable breadthFirstComparer =new Runnable(){
/** Sleep interval used when scan paused */
privateint interval = 1000;
/**
* Disables scanner-related buttons
*
*/
publicvoid stop(){
scanProgressLabel.setText("Scanning complete");
toggleScanButton.setEnabled(false);
}
/**
* Sleeps the currently running thread if user has paused the scan,
* otherwise initiates comparison of the trees' next depth node sets
* Compares two trees' set of nodes at every depth, highlighting
* nodes unique to each tree ( = distinct ) and recording for each
* tree the number of distinct nodes it contains. A node is
* non-distinct iff an identical plugin record is found (non-group
* plugin records have subrecords compared too)
*
*/
publicvoid run(){
while (!scanComplete){
System.out.println("comparing new depth");
//
// Compare a depth, highlighting distinct nodes from the
// two
// trees' node
// sets
//
for (DefaultMutableTreeNode nodeA : nodeSetA){
while (scanPaused){
try{
System.out.println("paused!");
Thread.sleep(interval);
}catch (Throwable exc){
Gecko.logException("Scanner thread could not be paused", exc);
}
}
System.out.println("1");
//
// If match not found, highlight
//
TreeNode[] pathNodesA = nodeA.getPath();
TreePath newSelectionPathA =new TreePath(pathNodesA);
if (!scannedPathsListA.contains(newSelectionPathA)){
scannedPathsListA.add(newSelectionPathA);
if (!nodeSetB.contains(nodeA)){
selectionPathsListA.add(newSelectionPathA);
}
}
}
for (DefaultMutableTreeNode nodeB : nodeSetB){
while (scanPaused){
try{
System.out.println("paused!");
Thread.sleep(interval);
}catch (Throwable exc){
Gecko.logException("Scanner thread could not be paused", exc);
}
}
System.out.println("2");
//
// If match not found, highlight
//
TreeNode[] pathNodesB = nodeB.getPath();
TreePath newSelectionPathB =new TreePath(pathNodesB);
if (!scannedPathsListB.contains(newSelectionPathB)){
scannedPathsListB.add(newSelectionPathB);
if (!nodeSetA.contains(nodeB)){
selectionPathsListB.add(newSelectionPathB);
}
}
}
//
// Iterate through nodes of each tree's column, adding
// direct
// descendants to new queue ready for next iteration
//
PluginRecordSet<DefaultMutableTreeNode> newNodeSetA =new PluginRecordSet<DefaultMutableTreeNode>();
for (DefaultMutableTreeNode nodeA : nodeSetA){
while (scanPaused){
try{
System.out.println("paused!");
Thread.sleep(interval);
}catch (Throwable exc){
Gecko.logException("Scanner thread could not be paused", exc);
}
}
System.out.println("3");
Enumeration childrenA = nodeA.children();
while (childrenA.hasMoreElements()){
DefaultMutableTreeNode childA = (DefaultMutableTreeNode) childrenA.nextElement();
newNodeSetA.add(childA);
}
}
PluginRecordSet<DefaultMutableTreeNode> newNodeSetB =new PluginRecordSet<DefaultMutableTreeNode>();
for (DefaultMutableTreeNode nodeB : nodeSetB){
while (scanPaused){
try{
System.out.println("paused!");
Thread.sleep(interval);
}catch (Throwable exc){
Gecko.logException("Scanner thread could not be paused", exc);
}
}
System.out.println("4");
Enumeration childrenB = nodeB.children();
while (childrenB.hasMoreElements()){
DefaultMutableTreeNode childB = (DefaultMutableTreeNode) childrenB.nextElement();
newNodeSetB.add(childB);
}
}
//
// Recurse if either queue has any elements left
//
int remainingA = newNodeSetA.size();
int remainingB = newNodeSetB.size();
if (!(remainingA + remainingB > 0)){
scanComplete =true;
}
synchronized (selectionPathsArrayA){
selectionPathsArrayA = selectionPathsListA.toArray(selectionPathsArrayA);
}
synchronized (selectionPathsArrayB){
selectionPathsArrayB = selectionPathsListB.toArray(selectionPathsArrayB);
}
nodeSetA = newNodeSetA;
nodeSetB = newNodeSetB;
}
//
// Stop thread execution once scan is complete
//
stop();
}
};
Here are the questions I can't seem to find definite answers for:
(1) I have a large number of operations occurring within my run() method, shown above. Because of this, and because I want the scanner that the code represents to be pausable, I've repeatedly put in tests for whether or not 'scanPaused' is true. Is this the best way to achieve an as-immediate-as-possible pausing of the thread's activities? Or is there some way, eg. by Interrupting the thread that I can code things to better serve my purpose?
(2) The access of two arrays 'selectionPathsArrayA' and 'selectionPathsArrayB', which can be seen at the bottom of the above code, is synchronized. Is this necessary, or redundant because thread io operations are automatically synchronized?
(3) I read in Roedy Green's Mindprod wait() and notify() pages that you shouldn't use these methods yourself. Is this the case? Because I've read many posts in these forums by seemingly knowledgeable people (hey, with 10k+ posts you have to know SOMETHING, right? :P) that advocate their use.
(4) Leading on from (3), would you use these methods on a lock object that you create?
(5) Is there some general rule for when to create lock objects? I'm fuzzy on where you should use synchronized access and where you should use locks... aren't they just one and the same in essence?
Thank you.

