This article has participated in the good article call order activity, click to see: back end, big front end double track submission, 20,000 yuan prize pool for you to challenge!
This is my fifth article in the Nuggets. Thanks for reading, and hope to continue to pay attention in the future, I will export more technical dry goods, we make progress together!
In the future, it will be divided into several major projects, such as concurrent projects, source code projects, interview projects, etc. (only the dry stuff will be shared).
If you are interested, you can click on my profile picture to view the historical articles, every one of them is dry goods!
Cut to the chase
How many ways do you know to create a thread?
We all know that implementing threads is the foundation of concurrent programming, because we have to implement multiple threads before we can proceed to the next set of operations. So today we’re going to start with the basics of concurrent programming, how to implement threads, which may seem simple and basic, but in fact, there’s a lot going on.
How many ways are there to implement threads? Most people will say there are two, three or four, and very few people will say one. Then you may say out of the mouth is two, first say you familiar with the two ways!
Implement the Runnable interface
public class RunnableThread implements Runnable {
@Override
public void run(a) {
System.out.println('Implementing a thread with a Runnable interface'); }}Copy the code
The first approach is to implement multithreading by implementing the Runnable interface. As shown in the code, first implement the Runnable interface with the RunnableThread class, and then override the run() method. All you need to do is pass the instance that implements the run() method to the Thread class to implement multithreading.
Thread class inheritance
public class ExtendsThread extends Thread {
@Override
public void run(a) {
System.out.println('Implement threads with Thread class'); }}Copy the code
The second approach is to inherit from Thread. As the code shows, unlike the first approach, it does not implement the interface. Instead, it inherits from Thread and overwrites its run() method. I’m sure you’re familiar with both of these techniques and use them frequently in your work.
Thread pools create threads
So why is there a third or fourth way? Let’s look at the third approach: creating threads from a thread pool. For example, if we set the number of threads in the thread pool to 10, there will be 10 child threads working for us. Next, let’s dig into the source code of the thread pool to see how the thread pool implements threads.
static class DefaultThreadFactory implements ThreadFactory { DefaultThreadFactory() { SecurityManager s = System.getSecurityManager(); group = (s ! =null)? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); namePrefix ="pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if(t.getPriority() ! = Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY);returnt; }}Copy the code
For a thread pool, threads are essentially created using a thread factory. The default is a DefaultThreadFactory, which sets some default values for the threads created in the pool, such as the name of the thread, whether it is a daemon, and the priority of the thread. But no matter how you set these properties, it still creates a Thread with new Thread(), but the constructor takes a few more arguments, so you can see that creating a Thread from a pool doesn’t break away from the two basic ways of creating a Thread. This is because it is essentially implemented through new Thread().
Callable creates a thread with a return value
class CallableTask implements Callable<Integer> {
@Override
public Integer call(a) throws Exception {
return newRandom().nextInt(); }}// Create a thread pool
ExecutorService service = Executors.newFixedThreadPool(10);
// Submit the task and return the result with a Future submit
Future<Integer> future = service.submit(new CallableTask());
Copy the code
A Runnable creates a thread that has no return value. A Callable and its associated Future and FutureTask can return the result of a thread’s execution as a return value, as shown in this code. Implement the Callable interface and set its generic type to Integer, and it will return a random number.
However, both Callable and FutureTask are tasks that need to be executed, just like Runnable, rather than threads themselves. They can be executed in a thread pool. As shown in the code, the submit() method puts the task into the thread pool, and the thread pool creates the thread. Either way, the thread ultimately executes the task, and the child thread is created in the same way as in the first two basic ways. This means implementing the Runnable interface and inheriting the Thread class. Yes, it’s Runnable and Thread
To echo the theme, there is only one way to implement threads
Instead of focusing on why there is only one way to create a Thread, let’s consider that there are two ways to create a Thread, and that other ways to create a Thread, such as a Thread pool or a timer, are simply a wrapper around new Thread().
If we call all of this a new way of doing things, then the way of creating threads will change a lot. For example, when the JDK is updated, it may add a few more classes, reencapsulate new Thread(), and appear to be a new way of implementing threads. You’ll find that they’re all ultimately implemented based on the Runnable interface or inheriting the Thread class!
@Override
public void run(a) {
if(target ! =null) { target.run(); }}Copy the code
To start the thread, start() calls the start() method, which eventually calls the run() method. Let’s take a look at how the run() method is implemented in the first method. The code for the run() method is very short. If (target! = null) to determine if the target is null. If it is not null, then line 2 is executed with target.run(), where the target is actually a Runnable. That is, the object passed to the Thread class when a Thread is implemented using the Runnable interface.
Then, let’s look at the second way, which is to inherit Thread. In fact, after inheriting Thread, we will override the run() method mentioned above, and the run() method will directly execute the task. But it will eventually need to call thread.start() to start the thread, and the start() method will eventually call the overridden run() method to perform its task.
At this point we can fully understand that there is actually only one way to create a Thread, and that is to construct a Thread class. This is the only way to create a Thread.
In essence, there is only one way to implement a Thread, and there are two ways to implement what the Thread executes. You can either implement the Runnable interface, or you can override the run() method by inheriting the Thread class, passing in the code that you want to execute, and having the Thread execute it, If we want more ways to implement threads, such as thread pools and timers, we just need to encapsulate them.
Implementing the Runnable interface is better than inheriting the Thread class to implement threads
If there is only one way to create a Thread, then it is better to implement the Runnable interface than to inherit the Thread class. What’s good about it? We from the following ways to analyze
-
In this case, Runnable is decoupled from the Thread class, which is responsible for starting the Thread and setting the properties of the Thread.
-
The second is to improve performance in some cases. If you want to execute a task, you need to create a separate Thread each time you execute the task, and then the Thread is destroyed at the end of its life cycle. If you want to execute the task again, you must create a new class that inherits Thread. If the execution of the content is less, such as just on the run () method simply print a line in the text, it brought about by the overhead is not large, compared to the whole thread was destroyed from the beginning to create has been completed, a series of operation than the run () method to print text itself bring spending far more, equivalent to pick up the sesame lost watermelon, the loss outweighs the gain If we implement the Runnable interface, we can pass the task directly into the thread pool and use a fixed number of threads to complete the task without having to create and destroy a thread each time, greatly reducing the performance overhead.
-
Third advantage is that the Java language does not support dual inheritance, if we once the class inherits the Thread class, then it further, there is no way to inherit the other classes, so that if the future we need to inherit this class on other classes implement some functions of expansion, there was no way to do it, is to limit code can expand sex in the future.
In conclusion, we should prefer to create threads by implementing the Runnable interface.
overtones
Learn a little bit, work a little bit, share a little bit, everybody come on!