I have this piece of code here that basically just creates a number of threads equal to the parameter, instantiates a MyRunnable object in each thread, adds the thread to a list of threads and then sets the name of the thread according to the iteration on the for loop.
public class ThreadManager {
protected List<Thread> threads;
public void createThreads(int number) {
for (int i = 0; i < number; i ) {
Thread thread = new Thread(MyRunnable::new);
threads.add(thread);
thread.setName("thread" i);
}
}
}
My question is if there's a cleaner way to do this? Is it possible to encapsulate this functionality in a lambda with the use of streams?
CodePudding user response:
Lambdas are non-transparent in regards to mutable local variables, control flow, and checked exceptions.
That's really bad... if the lambdas are guaranteed to run 'as you use them'. It's great if you are handing the lambdas off to other threads or contexts to run at completely different times. That explains why lambdas aren't transparent in those 3 regards: That's fantastic if the lambda runs out-of-context which they are designed to be capable of doing.
As a consequence, when the job you're using them for is guaranteed to run in context, lambdas bad. Sometimes they are the lesser evil, in which case its still worthwhile to run them, but, all other things being equal? Lambda bad.
So, why would you possibly be interested in writing this extremely simple code to lambdas? You'd just be making matters worse.
but, if you want to ignore all that, reduce your toolbox to just a hammer (lambdas/stream API), and thus use it for everything, even things hammers weren't really designed for:
threads = IntStream.of(0, number)
.mapToObj(i -> new Thread(new MyRunnable(), "thread" i))
.toList();
Note that you wrote MyRunnable::new
which isn't what you want.
MyRunnable::new
constructs an entirely new class (not your MyRunnable!) that implements Runnable and whose run()
method does:
- Create a new instance of MyRunnable.
- Toss it in the garbage.
- Return.
I rather doubt you wanted that. Just new MyRunnable()
will.. make a new myrunnable. Which is what you want, no?
I'd just do this instead:
for (int i = 0; i < number; i ) {
list.add(new Thread(new MyRunnable(), "thread" i));
}
Shorter, simpler to understand, more flexible for future changes (example: If ever you change things up and you throw some checked exceptions from inside there, that's no problem. Whereas with the lambda form you'd have to toss it in the bin and start over. Or even: You want to log something in addition to making a new thread object - in the lambda version you need to add braces and in general go to town on the structure. Whereas with the second snippet, just... add a log statement. No other changes required at all).
CodePudding user response:
You can do it like this:
public class ThreadManager {
protected List<Thread> threads;
public void createThreads(int number) {
IntStream.range(0, number).forEach(i -> threads.add(new Thread(MyRunnable::new, "thread" i)));
}
}
IntStream.range() creates stream, which will behave like your loop. To cite - Returns a sequential ordered IntStream from startInclusive (inclusive) to endExclusive (exclusive) by an incremental step of 1.
. The code certainly is shorter, but i highly doubt it will be cleaner.