0

Consider the following two designs of run method:

Approach A

public void run() {
    do {
        //do something
    } while (condition);
}

Approach B

public void run() {

    //do something...

    if (condition) {
       new Thread(this).start();
    }
}

The second approach seems cleaner to me, after some debate, I have been told it's not a good idea to use approach two.

Question:

  1. What are reasons (if there is any) that I shouldn't be using approach 2?
Ian2thedv
  • 2,691
  • 2
  • 26
  • 47
nafas
  • 5,283
  • 3
  • 29
  • 57

5 Answers5

4

You have two things here. A loop, and a method that continuously runs itself again in a new thread until a condition is met (not a loop).

If you need a loop, you would choose the standard normal loop that everyone understands and works perfectly.

If you need to write a weird piece of code that creates new threads for no reason, and makes other developers doubt your skills and understanding, you would go for option B.

There's absolutely no sense in your choice B unless there would be something additional like a queue or ThreadPoolExecutor involved for re-invoking the method, so the method would add this at the end for invocation at a later time, sort of like a "lazy loop".

Kayaman
  • 72,141
  • 5
  • 83
  • 121
  • 1
    B will behave like a loop where each iteration is run in a new thread. It is a recursive implementation of a loop. It would have made a lot more sense in Haskell and without the threading though. – Raniz Aug 12 '15 at 08:28
1

Because approach B uses one more thread than approach A. Creating threads is expensive, for a number of reasons @see Why is creating a Thread said to be expensive?

Approach A is also a little clearer to the reader, IMO. The simplest option usually is.

Community
  • 1
  • 1
Kieran
  • 343
  • 2
  • 10
  • 2
    Approach B does not use just 2 threads, it uses `N` (could be big). Of course, just 2 at a time. As for expensive, that's relative, depends on how much real work is being done inside of run. And it can be reduced to effectively zero by using a thread pool. But yeah, "the simplest option is usually the best". Don't use Threads, when you don't need to. Use loops for looping. – Thilo Aug 12 '15 at 08:21
  • Yes I see your point, I guess I meant that B uses one more thread than A. I'll update the answer. – Kieran Aug 12 '15 at 08:24
  • But B does _not_ use one more thread than A. Approach A runs in one thread. Approach B requires one thread for each execution of `//do something`. Approach B sequentially creates and destroys N threads where the example gives us no idea how large N might be. – Solomon Slow Aug 12 '15 at 14:33
  • Ok, although there's no indication that `//do something` is iterative. I think this answer is still ok, although I agree a Thread Pool is usually best with this kind of design. – Kieran Aug 12 '15 at 15:23
1

The 2nd option creates a new thread every time it is iterated, so it ends up being unnecessarily costly, especially when option A does the same thing but doesn't create new threads for every iteration.

0

The only good use-case I can find for Pattern B is if there is a significant delay before you want to re-run the method. For example for some kind of polling system that is supposed to run every X minutes until the system is being shut down.

In that case, using a scheduler instead of a Thread.sleep(fiveMinutes) makes sense to avoid tieing up resources unnecessarily (maybe you are holding on to a database connections or such).

Note that in that case, you'd be using a scheduler, not just Thread#start, so I am allowing for a rather liberal interpretation of Pattern B.

Thilo
  • 257,207
  • 101
  • 511
  • 656
  • Creating a new thread for each iteration is wasteful either way - you can just call `Thread.sleep` at the end of the loop. – Raniz Aug 12 '15 at 08:30
  • Well, I'd rather use a scheduler instead of long `Thread.sleeps`. That can actually reduce the number of threads used if I have multiple of these things running at the same time (they can all share one thread maybe). Not that creating a thread every five minutes is all that wasteful. – Thilo Aug 12 '15 at 08:33
  • Absolutely, but calling `new Thread(this).start()` and then letting the current thread die is weird, wasteful and unnecessary regardless of how often you do it. – Raniz Aug 12 '15 at 08:38
  • Yes. Like I said, I am allowing for a more liberal interpretation of Pattern B that will actually look more like `scheduler.schedule(new MyRunnable(), everyFiveMinutes)` with the exit condition being externalized as a `scheduler.shutdown()`. All I'm saying is "you need to have some rather special needs before a simple loop is not good enough anymore" and "there are fancy tools for those needs, so you don't just do Thread#start" – Thilo Aug 12 '15 at 08:43
  • Using thread creation as an alternative to `sleep()` is a _good_ use-case? – Solomon Slow Aug 12 '15 at 14:35
  • @jameslarge: Sure. If you have ten periodic jobs that need to do something short every five minutes, then using a scheduler to put them on one single thread instead of having ten threads sleeping 99% of the time seems like a good idea. – Thilo Aug 13 '15 at 01:24
  • Oops! I totally misread your first sentence, "The only good use-case for Pattern B..." I thought you were trying to imagine a reason why somebody would want to write Pattern B, and what you came up with was that some twisted soul might think of continually creating new threads just as a way to throttle the re-occurence of "//do something". Apparently, that was a product of my _own_ twisted mind. – Solomon Slow Aug 13 '15 at 13:24
0

They will behave very differently.

The first solution will loop until condition is false and then terminate.

The second solution will start a new thread and die until condition is false. It will likely accomplish what you want to do but it will waste a lot of resources allocating and destroying new threads.

Here's an example that loops over 5 values and prints the value and current thread name:

Loop:

Runnable loop = new Runnable() {

    int i = 0;

    @Override
    public void run() {
        do {
            System.out.printf("%s: %s%n", Thread.currentThread().getName(), i);
            i++;
        } while(i < 5);
    }
};
loop.run();

main: 0
main: 1
main: 2
main: 3
main: 4

Threaded:

Runnable thread = new Runnable() {

    int i = 0;

    @Override
    public void run() {
        System.out.printf("%s: %s%n", Thread.currentThread().getName(), i);
        i++;
        if(i < 5) {
            new Thread(this).start();
        }
    }
};
thread.run();

main: 0
Thread-0: 1
Thread-1: 2
Thread-2: 3
Thread-3: 4

As you can see, the threaded example prints each line on a different thread which is very wasteful and probably not what you want to accomplish.

Raniz
  • 10,882
  • 1
  • 32
  • 64