2

Possible Duplicate:
Java: Why not to start a thread in the constructor? How to terminate?

I'm used to run FindBugs on my code in order to find bugs or bad practice. Today it complains on the fact that I'm starting a thread in a class constructor.

Is really a bad thing to do? Could you explain me why?

It is at least safe if my class is final?

EDIT:

The thread is implemented as an inner class and it only uses fields of the main class already initialized when it is started:

public final class SingletonOuter {
    private static SingletonOuter ourInstance = new SingletonOuter();

    public static SingletonOuter getInstance() {
        return ourInstance;
    }

    private final SomeOtherClass aField;

    private SingletonOuter() {
        aField=new SomeOtherClass(); 
        thread=new InnerThread();
        thread.start();
    }

    private boolean pleaseStop;

    private synchronized boolean askedStop(){return pleaseStop;}
    public synchronized void stop(){
        pleaseStop=true;  
    }

    private final InnerThread thread ;
    private class InnerThread extends Thread{
        @Override public void run() {
            //do stuff with aField until askedStop()
        }
    }

}

EDIT:

I finally move the start of thread to the getInstance method, to avoid the possibility of introducing future bugs:

public final class SingletonOuter {
        private static SingletonOuter ourInstance

        public static SingletonOuter getInstance() {
            if (ourInstance==null){
                ourInstance= = new SingletonOuter();
                ourInstance.thread.start();
            }

            return ourInstance;
        }

        private final SomeOtherClass aField;

        private SingletonOuter() {
            aField=new SomeOtherClass(); 
            thread=new InnerThread();

        }
        ...
Community
  • 1
  • 1
Andrea Parodi
  • 5,534
  • 27
  • 46
  • The stated question is not an exact duplicated: I'm not escaping this nor asking for a way to stop my thread. I'll add code to clarify – Andrea Parodi May 25 '12 at 17:34
  • I've edited my answer now that we see the code. You are ok but only because `aField` is `final`. At least a big comment is warranted. See below. – Gray May 25 '12 at 17:44
  • my answer was incorrect. Although final fields are guaranteed to be initialized when the constructor returns, if you are forking your thread in the constructor, there is no guarantee that `afield` will be properly initialized. I've altered my answer. See: http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#finalRight – Gray May 29 '12 at 21:55

3 Answers3

5

Why it is bad practice to create a new thread on constructors?

Findbugs is alerting you to the problem with instruction reordering possibilities around object construction. Although the memory space for a new object is allocated, there is no guarantee that any of the fields have been initialized by the time your InnerThread has been started. Although the final fields will be initialized before the constructor finishes, there is no guarantee that if InnerThread starts to use (for example) aField when it starts, that it will be initialized. The Java compiler does this for performance reasons. It also has the option to move the initialization of non-final fields to after the new instance has been returned by the constructor.

If you start a new thread in the constructor then there is a chance that the thread will be dealing with a partially initialized object. Even if the thread.start() is the last statement in your constructor, the new thread may be accessing a partially constructed object because of the reordering. This is part of the Java language specification.

Here's a good link about the topic: calling thread.start() within its own constructor

It mentions the following:

By starting it from within the constructor, you are guaranteed to violate the Java Memory Model guidelines. See Brian Goetz's Safe Construction Techniques for more info.

Edit:

Since you code is starting a new thread that is accessing afield, according to the Java Memory Model there is no guarantee that afield will be properly initialized when the thread starts to run.

What I would recommend instead is to add a start() method on your class that calls the thread.start(). This is a better practice and makes it more visible to other classes that are using this class that a thread is created in the constructor.

Community
  • 1
  • 1
Gray
  • 115,027
  • 24
  • 293
  • 354
  • I'm setting up all fields of the outer class used by the inner class thread in the constructor itself, before I start the thread. There is still a possibility that the compiler change my instruction order? – Andrea Parodi May 25 '12 at 17:29
  • 1
    "_guaranteed_ to violate the guidelines" doesn't leave much margin for flexibility. – Louis Wasserman May 25 '12 at 17:32
  • Yes. There is no guarantee that any of the non-final fields will be properly initialized before the constructor finishes. That's the whole point of the race. That means that your other thread may deal with an object that has not been fully initialized even if it is started on the last line of the constructor. – Gray May 25 '12 at 17:36
  • Thank you Gray, you've bean very clear. I think I'll move the thread start to the static accessor of the singleton – Andrea Parodi May 25 '12 at 17:47
  • 1
    My understanding is that not even final fields are will be safely initialized if a thread is started in a constructor. All the safe publication rules for final variables are voided if a reference to the object escapes the constructor. – Michael Krussel May 29 '12 at 21:28
  • Good point @Michael. I'll change my answer to make this more explicit and correct the Edit. – Gray May 29 '12 at 21:52
  • *Even if the `thread.start()` is the last statement in your constructor, the new thread may be accessing a partially constructed object because of the reordering.* I think you're mistaken. `Thread.start()` creates a memory barrier that would prevent reordering or visibility problems. – shmosel Jul 09 '17 at 05:15
2

In general, it is best to be gentle with what you do in a constructor.

Your object is still in an invalid state, so you don't want anybody to have access to it. When you start a thread from a constructor, it will likely have a reference to the object that is being constructed (otherwise why is the constructor starting it?). This reference will point to an invalid object when the thread starts and soon after it will become valid. There is an immediate potential for horrible race conditions there.

Here is a link to a good article about it http://www.ibm.com/developerworks/java/library/j-jtp0618/index.html

MK.
  • 33,605
  • 18
  • 74
  • 111
  • Thank you, I'll read it. I'm aware of the necessity to be gentle in my constructors. The thread is an inner class and only use fields of the main class already initialized when it is started. – Andrea Parodi May 25 '12 at 17:23
  • that's even worse, because inner class has full access to everything. It might work ok, but you are exposing yourself to all kinds of terrible things. – MK. May 25 '12 at 17:37
0

Every time you instantiate that class you create a thread. Threads are expensive and very difficult to test. If you instantiate many objects you will run into performance problems, you should consider a ThreadPool to fix a limit on the number of threads. Also, if you will have trouble trying to unit test whatever behavior is happening in the thread.

Garrett Hall
  • 29,524
  • 10
  • 61
  • 76
  • I think Andrea Parodi is asking for a comparison of `Thread t = new Runnable ( ) { public void run ( ) { /*do stuff*/} } ; t . start ( ) ;` versus `new Runnable ( ) { { start ( ) ; } public void run ( ) { /*do stuff*/} } ;`. In both cases, the expense of threads is the same. If for example you are using anonymous classes to create throwaway threads, why not just start the thread in its initialization block and do away with the need to even keep a reference to it. It is something I have done before, and now thanks to other answers, I know why not. – emory May 25 '12 at 17:27
  • @Garret: the class from which Im starting the thread is a singleton, so your problem does not apply. – Andrea Parodi May 25 '12 at 17:31