I am doing a hello world on threads, i have created a simple thread with using the run()
call (which it is just a normal method call) and a duplicate thread with using the start() call which spawns another thread to process, however, the time taken for the start()
calls is more than that taken for the run()
calls, which are not threaded calls, why is it so?
Start call time taken: 00:00:08:300
long tim开发者_运维问答e = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
Thread thread = new Thread(new Car());
thread.setName(Integer.toString(i));
thread.start();
}
long completedIn = System.currentTimeMillis() - time;
System.out.println(DurationFormatUtils.formatDuration(completedIn,
"HH:mm:ss:SS"));
Run call time taken: 00:00:01:366
long time = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
Thread thread = new Thread(new Car());
thread.setName(Integer.toString(i));
thread.run();
}
long completedIn = System.currentTimeMillis() - time;
System.out.println(DurationFormatUtils.formatDuration(completedIn,
"HH:mm:ss:SS"));
From comment to accepted response: "Would you have any advise on this?"
Yes, do not use threads directly. Since java 5, we have the java.util.concurrent framework that allows to simplify thread and task management.
Creating a thread is costly.
That is even before the thread will run the task you want, it will have to be created, a really long process. For that very reason, we have the concept of thread pool.
Instead of creating new threads each time you want to perform a concurrent task, you first create the threads and then you send them tasks when you need too.
A thread pool as a second advantage. When the task is finished, the thread is not destroyed, but kept active to run the next task, so the thread creation cost occurs only one time, at initialization time.
So how do you use theses concepts?
First create an executor with a thread pool. To stay simple we create a thread pool with 100 threads (as you want to simulate load of 100 concurrent calls):
ExecutorService pool = Executors.newFixedThreadPool(100);
Then you'ill submit your tasks:
long time = System.currentTimeMillis();
for (int i=0; i<100000; i++) {
pool.execute(new Car());
}
And important, you'll wait for all tasks to finish before stopping the program !!!
pool.shutdown(); //Do no longer accept new tasks.
pool.awaitTermination(1, TimeUnit.HOURS); //Wait for up to one hour for all tasks to finish.
long completedIn = System.currentTimeMillis() - time;
System.out.println(DurationFormatUtils.formatDuration(completedIn,
"HH:mm:ss:SS"));
In your thread code you didn't wait for the threads to finish their job, in fact you measured the thread creation time, not the tasks running time.
What the code does?
The executor.execute method execute the provided task inside a thread. Here it take one of the 100 thread, and let it execute the task.
What happens if there is more than 100 tasks?
With 100 threads, you can't run more than 100 concurrent tasks. Other tasks will be queued untill one task is finished so one thread is available to execute it. This is a good way to ensure you don't create too much thread and OutOfMemory or other nasty things don't happens.
How many thread should you use?
This depend of the kind of tasks you want to execute.
If it is like a web server, you are mostly IO bound waiting for the database to get data and then for the network to send the response, your thread will be mostly waiting. So even one CPU will benefit from dozen, even hundred of threads. And even if more thread start to slow down the whole application, it allow to process the user request instead of making it wait, or just deny to respond.
If your task are CPU bound you'll want something like one task per CPU core so to maximize usage of your hardware but limit the overhead of context switches. With a 4 core hyper-threading CPU, you can go up to 8 concurrent threads.
This response is just a short introduction... You'll learn more by looking at java.util.concurrent package and reading some tutorials.
start
actually creats a new thread (heavy operation) while run
calls the method run
of the thread object on the current thread (simple method call - light operation)
From docs of Thread regarding start
:
Causes this thread to begin execution; the Java Virtual Machine calls the run method of this thread. The result is that two threads are running concurrently: the current thread (which returns from the call to the start method) and the other thread (which executes its run method).
You should never call run
directly. What you've done there is create a bunch of Thread
objects, but you never actually create new threads; you just run the code on the main thread instead (because you're calling run
directly).
Creating 100,000 threads will not perform well on most current computers (I'm leaving out high-end multi-million-dollar machines). As soon as you have more threads than you have CPU capacity to support, you start causing context-switches. So if you have a quad-core system, running more than four threads will actually slow your program down (modulo I/O operations and such where the CPU would be idling anyway).
When you start a bunch of threads and you have only one or two cores, the time spent context switching plus running the tasks can easily exceed the time it takes to run the code sequentially, one task after another.
The start
method is where the magic is. It spawns a new thread it this takes time:
- create the thread
- start the thread
- manage thread lifecycle
The best explanation will be found in the sources of java.lang.Thread
.
The run method is what a thread launches, but it is just a simple method of the Runnable interface.
The Thread.start() method will create a new thread (of execution, a real one) that will execute the run() method of your thread instance.
The run() method can be called on a thread instance because there are 2 ways in which we can define what a thread of execution will execute.
The old one: extending Thread's run method. It's not advised to use that anymore for some reasons including coupling.
The new one: passing a Runnable (which provides the run() method) to the constructor of the thread.
Note that if you create a thread with a Runnable (target) and also override the run() method of the thread, you can understand you are currently providing 2 codes to execute, but which one will be executed? That's easy to find if you look at the default run() implementation of a Thread:
public void run() {
if (target != null) {
target.run();
}
}
If you override that method, the provided Runnable target will be bypassed unless you call it on your override.
As i said there are 2 ways to start a thread. I guess when they designed the "new way" they had to keep that run() method in the Thread class for retrocompatibility issues, but if they could go back in time they perhaps would not make the Thread class implement the Runnable interface so that there would be less confusion about how threads must be started...
精彩评论