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.

[10662 byte] By [KomodoDavea] at [2007-11-26 13:42:56]
# 1

Hello,

I won't tell you how to fix your program because there are many tutorials that can show you better than I can. Look up the "producer/consumer problem".

For motivation, I would encourage you to use wait() and notify() instead of busy waiting (polling), which is what you are doing.

Busy waiting is a very simple concept that works so you can do thread programming right away. However, it is totally inefficient, so you should try to learn how to use wait/notify since once you learn that you will see that not only is it thousands of times faster (not joking!), but you never have to use busy waiting (polling) again.

You can use semaphores for simple thread programming (semaphores take care of the wait/notify for you). Most threading courses begin with a couple weeks of semaphore programming.

http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/Semaphore.html

jvaudrya at 2007-7-8 1:16:08 > top of Java-index,Desktop,Core GUI APIs...
# 2
Read the Thread API. The suspend() method would seem to be what you want. Even though its deprecated, the API takes you to a link that show an alternative approach, which is basically described in the previous posting.
camickra at 2007-7-8 1:16:08 > top of Java-index,Desktop,Core GUI APIs...
# 3

Thank you very much for your responses guys.

jvaudry, I appreciate the detailed reply. I will certainly research carefully what you have suggested.

Camickr, as stated in my thread title, I _have_ done my research (minus the jvaudry suggestions I wasn't aware of, of course ), and so naturally I have already read the Thread API and links within. The document that you refer to states this for a suspend() alternative:

"As with Thread.stop, the prudent approach is to have the "target thread" poll a variable indicating the desired state of the thread (active or suspended). When the desired state is suspended, the thread waits using Object.wait. When the thread is resumed, the target thread is notified using Object.notify.

My code above already polls a variable, as pointed out by jvaudry. If you'll look at my 3rd point, you'll see why I chose not to use wait() and notify() as suggested :) Am I to take it from jvaudry's response that using wait() and notify() is a perfectly acceptable practise, and so I should ignore what I read on mindprod.com ?

Also, would anyone be kind enough to answer my remaining questions? Nothing I've read regarding threads has given me confidence about disposing of my synchronized wrapper for the array access at the bottom. Is it just data io that threads automatically synchronise on? Does that include variables?

I'm also still unsure about when to use lock objects in place of (or in parallel with) synchronized methods and statements, even after reading even more Sun articles since posting this. A simple verbal description of a generic scenario where a lock object is beneficial would be most welcome. I have read what Sun have to say about such objects, but fail to see how this is any different from just using a synchronized wrapper for a field/object.

Many thanks.

KomodoDavea at 2007-7-8 1:16:08 > top of Java-index,Desktop,Core GUI APIs...