Question on Daemon thread

A

ankur

This code defines 3 threads A, B and C as Daemon threads. However they
continue to push and pop on the Stack even after the main thread (user
thread) has died. How is that possible ? Because Daemon threads exist
only so long as user threads exist..isn't it ?

package pack4;

class StackImpl1 {
private Object[] stackArray;
private volatile int topOfStack;

StackImpl1 (int capacity) {
stackArray = new Object[capacity];
topOfStack = -1;
}

public synchronized Object pop() {
System.out.println(Thread.currentThread() + ": popping");

while (isEmpty())
try {
System.out.println(Thread.currentThread() + ": waiting
to pop");
wait(); // (1)
} catch (InterruptedException e) { }
Object obj = stackArray[topOfStack];
stackArray[topOfStack--] = null;
System.out.println(Thread.currentThread() + ": notifying after
pop");
notify(); // (2)
return obj;
}

public synchronized void push(Object element) {
System.out.println(Thread.currentThread() + ": pushing");
while (isFull())
try {
System.out.println(Thread.currentThread() + ": waiting
to push");
wait(); // (3)
} catch (InterruptedException e) { }
stackArray[++topOfStack] = element;
System.out.println(Thread.currentThread() + ": notifying after
push");
notify(); // (4)
}

public boolean isFull() { return topOfStack >= stackArray.length
-1; }
public boolean isEmpty() { return topOfStack < 0; }
}

abstract class StackUser extends Thread { // (5) Stack user

protected StackImpl1 stack; // (6)

StackUser(String threadName, StackImpl1 stack) {
super(threadName);
this.stack = stack;
System.out.println(this);
setDaemon(true); // (7) Daemon
thread
start(); // (8) Start this
thread
}
}

class StackPopper extends StackUser { // (9) Popper
StackPopper(String threadName, StackImpl1 stack) {
super(threadName, stack);
}
public void run() { while (true) stack.pop(); }
}

class StackPusher extends StackUser { // (10) Pusher
StackPusher(String threadName, StackImpl1 stack) {
super(threadName, stack);
}
public void run() { while (true) stack.push(new Integer(1)); }
}

public class WaitAndNotifyClient {
public static void main(String[] args)
throws InterruptedException { // (11)

StackImpl1 stack = new StackImpl1(5);

new StackPusher("A", stack);

new StackPusher("B", stack);

new StackPopper("C", stack);

System.out.println("Main Thread sleeping.");
Thread.sleep(10);
System.out.println("Exit from Main Thread.");
}
}


Output : Observe that the pushing and popping continues even after the
main thread has exited !

Thread[A,5,main]
Thread[B,5,main]
Thread[C,5,main]
Main Thread sleeping.
Thread[A,5,main]: pushing
Thread[A,5,main]: notifying after push
Thread[A,5,main]: pushing
Thread[A,5,main]: notifying after push
Thread[A,5,main]: pushing
Thread[A,5,main]: notifying after push
Thread[A,5,main]: pushing
Thread[A,5,main]: notifying after push
Thread[C,5,main]: popping
Thread[C,5,main]: notifying after pop
Thread[C,5,main]: popping
Thread[C,5,main]: notifying after pop
Thread[C,5,main]: popping
Thread[C,5,main]: notifying after pop
Thread[C,5,main]: popping
Thread[C,5,main]: notifying after pop
Thread[B,5,main]: pushing
Thread[B,5,main]: notifying after push
Thread[B,5,main]: pushing
Thread[B,5,main]: notifying after push
Thread[B,5,main]: pushing
Thread[B,5,main]: notifying after push
Thread[B,5,main]: pushing
Thread[B,5,main]: notifying after push
Thread[A,5,main]: pushing
Thread[A,5,main]: notifying after push
Thread[A,5,main]: pushing
Thread[A,5,main]: waiting to push
Thread[C,5,main]: popping
Thread[C,5,main]: notifying after pop
Thread[C,5,main]: popping
Thread[C,5,main]: notifying after pop
Thread[C,5,main]: popping
Thread[C,5,main]: notifying after pop
Thread[C,5,main]: popping
Thread[C,5,main]: notifying after pop
Thread[A,5,main]: notifying after push
Thread[A,5,main]: pushing
Thread[A,5,main]: notifying after push
Thread[B,5,main]: pushing
Thread[B,5,main]: notifying after push
Thread[B,5,main]: pushing
Thread[B,5,main]: notifying after push
Thread[B,5,main]: pushing
Thread[B,5,main]: waiting to push
Thread[C,5,main]: popping
Thread[C,5,main]: notifying after pop
Thread[C,5,main]: popping
Thread[C,5,main]: notifying after pop
Thread[C,5,main]: popping
Thread[C,5,main]: notifying after pop
Thread[C,5,main]: popping
Thread[C,5,main]: notifying after pop
Thread[C,5,main]: popping
Exit from Main Thread.---------------------------------------------
Thread[C,5,main]: notifying after pop
Thread[C,5,main]: popping
Thread[C,5,main]: waiting to pop
Thread[B,5,main]: notifying after push
Thread[C,5,main]: notifying after pop
Thread[C,5,main]: popping
Thread[C,5,main]: waiting to pop
Thread[A,5,main]: pushing
Thread[A,5,main]: notifying after push
Thread[A,5,main]: pushing
Thread[A,5,main]: notifying after push
Thread[A,5,main]: pushing
Thread[A,5,main]: notifying after push
Thread[A,5,main]: pushing
Thread[A,5,main]: notifying after push
Thread[A,5,main]: pushing
Thread[A,5,main]: notifying after push
Thread[C,5,main]: notifying after pop
Thread[B,5,main]: pushing
Thread[B,5,main]: notifying after push
Thread[B,5,main]: pushing
Thread[B,5,main]: waiting to push
Thread[C,5,main]: popping
Thread[C,5,main]: notifying after pop
Thread[B,5,main]: notifying after push
Thread[B,5,main]: pushing
Thread[B,5,main]: waiting to push
Thread[C,5,main]: popping
Thread[C,5,main]: notifying after pop
Thread[B,5,main]: notifying after push
Thread[B,5,main]: pushing
Thread[B,5,main]: waiting to push
Thread[C,5,main]: popping
Thread[C,5,main]: notifying after pop
Thread[B,5,main]: notifying after push
Thread[B,5,main]: pushing
Thread[B,5,main]: waiting to push
Thread[C,5,main]: popping
Thread[C,5,main]: notifying after pop
Thread[B,5,main]: notifying after push
Thread[B,5,main]: pushing
Thread[B,5,main]: waiting to push
Thread[C,5,main]: popping
Thread[C,5,main]: notifying after pop
Thread[B,5,main]: notifying after push
Thread[B,5,main]: pushing
Thread[B,5,main]: waiting to push
Thread[A,5,main]: pushing
Thread[A,5,main]: waiting to push
Thread[C,5,main]: popping
Thread[C,5,main]: notifying after pop
Thread[B,5,main]: notifying after push
Thread[B,5,main]: pushing
Thread[B,5,main]: waiting to push
Thread[A,5,main]: waiting to push
Thread[C,5,main]: popping
Thread[C,5,main]: notifying after pop
 
J

John B. Matthews

ankur said:
This code defines 3 threads A, B and C as Daemon threads. However
they continue to push and pop on the Stack even after the main thread
(user thread) has died. How is that possible ? Because Daemon threads
exist only so long as user threads exist..isn't it ?

Let's see:

<http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.8>

I would say your program's execution is compliant. It just takes a
little time for the VM to reap your doomed daemons; or you can
foreclose explicitly, as shown in this nicely wrapped version
of your example:

<sscce>
public class WaitAndNotifyClient {

public static void main(String[] args)
throws InterruptedException {

StackImpl1 stack = new StackImpl1(5);

new StackPusher("A", stack);
new StackPusher("B", stack);
new StackPopper("C", stack);

System.out.println("Main Thread sleeping.");
Thread.sleep(10);
System.out.println("Exit from Main Thread.");
System.exit(1);
}
}

class StackImpl1 {

private Object[] stackArray;
private volatile int topOfStack;

StackImpl1(int capacity) {
stackArray = new Object[capacity];
topOfStack = -1;
}

public synchronized Object pop() {
System.out.println(Thread.currentThread()
+ ": popping");

while (isEmpty()) {
try {
System.out.println(Thread.currentThread()
+ ": waiting to pop");
wait();
} catch (InterruptedException e) {
}
}
Object obj = stackArray[topOfStack];
stackArray[topOfStack--] = null;
System.out.println(Thread.currentThread()
+ ": notifying after pop");
notify();
return obj;
}

public synchronized void push(Object element) {
System.out.println(Thread.currentThread()
+ ": pushing");
while (isFull()) {
try {
System.out.println(Thread.currentThread()
+ ": waiting to push");
wait();
} catch (InterruptedException e) {
}
}
stackArray[++topOfStack] = element;
System.out.println(Thread.currentThread()
+ ": notifying after push");
notify();
}

public boolean isFull() {
return topOfStack >= stackArray.length - 1;
}

public boolean isEmpty() {
return topOfStack < 0;
}
}

abstract class StackUser extends Thread {

protected StackImpl1 stack;

StackUser(String threadName, StackImpl1 stack) {
super(threadName);
this.stack = stack;
System.out.println(this);
setDaemon(true);
start();
}
}

class StackPopper extends StackUser {

StackPopper(String threadName, StackImpl1 stack) {
super(threadName, stack);
}

public void run() {
while (true) {
stack.pop();
}
}
}

class StackPusher extends StackUser {

StackPusher(String threadName, StackImpl1 stack) {
super(threadName, stack);
}

public void run() {
while (true) {
stack.push(new Integer(1));
}
}
}
</sscce>
 
A

ankur

 ankur said:
This code defines 3 threads A, B and C as Daemon threads. However
they continue to push and pop on the Stack even after the main thread
(user thread) has died. How is that possible ? Because Daemon threads
exist only so long as user threads exist..isn't it ?

Let's see:

<http://java.sun.com/docs/books/jls/third_edition/html/execution.html#...>

I would say your program's execution is compliant. It just takes a
little time for the VM to reap your doomed daemons; or you can
foreclose explicitly, as shown in this nicely wrapped version
of your example:

<sscce>
public class WaitAndNotifyClient {

    public static void main(String[] args)
        throws InterruptedException {

        StackImpl1 stack = new StackImpl1(5);

        new StackPusher("A", stack);
        new StackPusher("B", stack);
        new StackPopper("C", stack);

        System.out.println("Main Thread sleeping.");
        Thread.sleep(10);
        System.out.println("Exit from Main Thread.");
        System.exit(1);
    }

}

class StackImpl1 {

    private Object[] stackArray;
    private volatile int topOfStack;

    StackImpl1(int capacity) {
        stackArray = new Object[capacity];
        topOfStack = -1;
    }

    public synchronized Object pop() {
        System.out.println(Thread.currentThread()
            + ": popping");

        while (isEmpty()) {
            try {
                System.out.println(Thread.currentThread()
                    + ": waiting to pop");
                wait();
            } catch (InterruptedException e) {
            }
        }
        Object obj = stackArray[topOfStack];
        stackArray[topOfStack--] = null;
        System.out.println(Thread.currentThread()
            + ": notifying after pop");
        notify();
        return obj;
    }

    public synchronized void push(Object element) {
        System.out.println(Thread.currentThread()
            + ": pushing");
        while (isFull()) {
            try {
                System.out.println(Thread.currentThread()
                    + ": waiting to push");
                wait();
            } catch (InterruptedException e) {
            }
        }
        stackArray[++topOfStack] = element;
        System.out.println(Thread.currentThread()
            + ": notifying after push");
        notify();
    }

    public boolean isFull() {
        return topOfStack >= stackArray.length - 1;
    }

    public boolean isEmpty() {
        return topOfStack < 0;
    }

}

abstract class StackUser extends Thread {

    protected StackImpl1 stack;

    StackUser(String threadName, StackImpl1 stack) {
        super(threadName);
        this.stack = stack;
        System.out.println(this);
        setDaemon(true);
        start();
    }

}

class StackPopper extends StackUser {

    StackPopper(String threadName, StackImpl1 stack) {
        super(threadName, stack);
    }

    public void run() {
        while (true) {
            stack.pop();
        }
    }

}

class StackPusher extends StackUser {

    StackPusher(String threadName, StackImpl1 stack) {
        super(threadName, stack);
    }

    public void run() {
        while (true) {
            stack.push(new Integer(1));
        }
    }}

</sscce>

Sorry John, I did not understand completely what you were trying to
say. Could you be a little more explicit with the explanation. Did you
modify my program in some way ?
 
J

John B. Matthews

ankur said:
 ankur said:
This code defines 3 threads A, B and C as Daemon threads. However
they continue to push and pop on the Stack even after the main thread
(user thread) has died. How is that possible ? Because Daemon threads
exist only so long as user threads exist..isn't it ?
[...]
I would say your program's execution is compliant. It just takes a
little time for the VM to reap your doomed daemons; or you can
foreclose explicitly, as shown in this nicely wrapped version
of your example:
[...]
Sorry John, I did not understand completely what you were trying to
say. Could you be a little more explicit with the explanation.

<http://java.sun.com/docs/books/jls/third_edition/html/execution.html>

See section 12.8; it's quite short. Your version terminates the first
way; mine the second. I found it instructive to profile your version.
Did you modify my program in some way?

Yes, as described in section 12.8, item 2. In addition, I reordered the
class declarations and wrapped the listing to fit the column limit
common to newsreaders. I did this so that others can try your code
without repeating my effort, as my interpretation of your observations
may benefit from correction or amplification.

[Please trim sigs.]
 
A

ankur

 ankur said:
<[email protected]>,
This code defines 3 threads A, B and C as Daemon threads. However
they continue to push and pop on the Stack even after the main thread
(user thread) has died. How is that possible ? Because Daemon threads
exist only so long as user threads exist..isn't it ?
[...]
I would say your program's execution is compliant. It just takes a
little time for the VM to reap your doomed daemons; or you can
foreclose explicitly, as shown in this nicely wrapped version
of your example:
[...]
Sorry John, I did not understand completely what you were trying to
say. Could you be a little more explicit with the explanation.

<http://java.sun.com/docs/books/jls/third_edition/html/execution.html>

See section 12.8; it's quite short. Your version terminates the first
way; mine the second. I found it instructive to profile your version.
Did you modify my program in some way?

Yes, as described in section 12.8, item 2. In addition, I reordered the
class declarations and wrapped the listing to fit the column limit
common to newsreaders. I did this so that others can try your code
without repeating my effort, as my interpretation of your observations
may benefit from correction or amplification.

[Please trim sigs.]

Hi John,

But I still did not understand why the threads A, B, C stay active
even after the main thread has terminated and System.exit(1) has also
been called!

I think you mentioned something to this effect when you said : "It
just takes a
little time for the VM to reap your doomed daemons; ". Can you
elaborate please ?

Thanks,
Ankur
 
J

Joshua Cranmer

ankur said:
I think you mentioned something to this effect when you said : "It
just takes a
little time for the VM to reap your doomed daemons; ". Can you
elaborate please ?

After the main thread terminates, the VM takes some time to clean up the
daemon threads; part of the thread destruction probably involves some GC
runs (finalizers might verify this, but the nature of how finalizers are
invoked could cause problems).

The threads are, therefore, still running while the VM gets around to
cleaning up all the rapidly-dying threads.
 
J

John B. Matthews

ankur said:
 ankur said:
This code defines 3 threads A, B and C as Daemon threads. However
they continue to push and pop on the Stack even after the main thread
(user thread) has died. How is that possible ? Because Daemon threads
exist only so long as user threads exist..isn't it ?
[...]
I would say your program's execution is compliant. It just takes a
little time for the VM to reap your doomed daemons; or you can
foreclose explicitly, as shown in this nicely wrapped version
of your example:
[...]
Sorry John, I did not understand completely what you were trying to
say. Could you be a little more explicit with the explanation.

<http://java.sun.com/docs/books/jls/third_edition/html/execution.html>

See section 12.8; it's quite short. Your version terminates the first
way; mine the second. I found it instructive to profile your version.
Did you modify my program in some way?

Yes, as described in section 12.8, item 2. In addition, I reordered the
class declarations and wrapped the listing to fit the column limit
common to newsreaders. I did this so that others can try your code
without repeating my effort, as my interpretation of your observations
may benefit from correction or amplification.

[Please trim sigs.]
But I still did not understand why the threads A, B, C stay active
even after the main thread has terminated and System.exit(1) has also
been called!

Well, when your non-daemon thread concludes execution, the daemon
threads are still running. What does section 12.8 say happens next?
I think you mentioned something to this effect when you said : "It
just takes a little time for the VM to reap your doomed daemons; ".
Can you elaborate please?

I don't know what else to say: Section 12.8 doesn't specify how it
happens, only that it does, eventually. I presume that it takes some
finite amount of time after the last line of main() executes, during
which a number of pushes and pops occur.

If I had to guess, based on the timeline, a thread named DestroyJavaVM
starts waiting just about the time main() ends. What do you see when you
profile your code?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top