Several methods of multithread switching in Android

We know that multithreading is a necessary scenario in Android development. Many native APIs and open source projects have multithreading content. Here is a brief summary and discussion of common multithreading switching methods.

We first review several basic contents of Java multithreading, and then analyze and summarize the implementation of thread switching in some classic codes.

Several foundations

Multi thread switching can be roughly divided into the following contents: how to start multiple threads, how to define the tasks of each thread, and how to communicate with each other among threads.

Thread

Thread can solve the problem of opening multiple threads.

Thread is a thread class that implements multithreading in Java. Each thread object can start a new thread. Note that you can start or not start a new thread:

In addition, there is a thread priority problem with threads. If you set a higher thread priority for threads, you will have the opportunity to obtain more CPU resources. Note that there is also an opportunity here. Threads with higher priority are not necessarily executed before other threads, but the system tends to allocate more CPUs to them.

By default, the thread priority of the new thread is the same as that of the current thread.

There are two ways to set thread priority:

These two settings are relatively independent. In Android, it is generally recommended to set the priority through the process process.

ThreadPool

Thread itself needs to occupy memory. Opening / destroying excessive working threads will cause excessive resource loss. In this scenario, we will generally optimize through resource reuse. For IO resources, we will do IO reuse (such as HTTP keepalive). For memory, we will do memory pool reuse (such as fresco's memory pool). For CPU resources, We usually do thread reuse, that is, thread pool.

Therefore, in Android development, generally, a large number of threads will not be opened directly, but ThreadPool will be used to reuse threads.

Runnable

Runnable mainly solves the problem of how to define the work task of each thread. Runnable is an interface that implements multithreading in Java. Compared with thread, runnable interface is easier to extend (without single inheritance). Moreover, thread itself is also a runnable:

Compared with thread, runnable does not pay attention to how to schedule threads, but only how to define the work tasks to be executed. Therefore, in actual development, runnable interface is used to complete multi-threaded development.

Callable

Callable is basically similar to runnable, but callable can return execution results.

Inter thread communication

Thread and runnable can switch to another thread to work (runnable needs to assign additional working threads), but they will exit after completing the task and do not pay attention to how to realize communication between threads. Therefore, when switching threads, they also need communication between threads, which requires some inter thread communication mechanisms.

Future

Generally speaking, if we want to do simple communication, we most often use interface callback to realize it.

Future is such an interface, which can partially solve the problem of thread communication. The future interface defines callback functions such as done and cancelled. When the task of the working thread is completed, it will notify us through callback (in the working thread), and we will notify other threads by other means.

Condition

Condition is actually used with lock, but it makes sense to regard it as a tool for inter thread communication.

Because condition itself is a tool for coordinating communication among multiple threads, condition can wake up waiting threads under certain conditions.

Handler

In fact, the most complete inter thread communication mechanism is also the most familiar inter thread communication mechanism. There is nothing better than the handler communication mechanism. The handler uses the thread closed ThreadLocal to maintain a message queue. The core of the handler is to pass messages through this message queue to realize inter thread communication.

Multithreading switching of asynctask

After reviewing several basic concepts of multithreading, let's take a look at the simple multithreading switching, the asynctask that comes with Android.

Asynctask mainly defines the work content of the working thread in the doinbackground function, and defines the work content of the main thread in other functions, such as

Onpostexecute, which must involve two problems: 1. How to throw doinbackground to the working thread; 2. How to throw onpostexecute to the main thread

Actually, it's very simple. Let's look at the first one first

1. How to throw doinbackground to a worker thread

When using asynctask, we will generally create an asynctask based extension class or anonymous class to implement several abstract functions, such as:

Then, we will instantiate the asynctask:

In the source code of asynctask, we can see that a workerrunnable will be created in the constructor:

Workerrunnable is actually a callable object. Therefore, doinbackground is wrapped in a callable object. The callable will continue to be wrapped and finally handed over to a thread pool for execution:

The process is as follows:

Define that doinbackground -- > is called by a callable -- > and packaged into a runnable -- > and handed over to the thread pool for execution.

This solves the first problem, how to throw doinbackground to the worker thread.

Let's look at the second question.

2. How to throw onpostexecute to the main thread

First of all, we need to know when the work task is completed. We need to trigger an interface callback when the work is completed, that is, the future mentioned above. Still look at the source code of asynctask:

In this way, we know that we can handle the onpostexecute function, but we also need to throw it to the main thread. The main source code is as follows:

From the source code, we can see that asynctask throws the task to the main thread through the handler mechanism.

Generally speaking, the multithreaded task of asynctask is a working thread implemented through the thread pool. After completing the task, it uses the done callback of future to notify the completion of the task, and notifies the main thread to execute onpostexecute and other callback functions through the handler mechanism.

Multithreading switching of eventbus

Eventbus will register a target thread for each subscription event, so it needs to switch to the target thread in real time according to the registration information from the thread publishing the event. Therefore, this is a typical multi-threaded switching scenario. According to the source code of eventbus, the main judgment codes for multithread switching are as follows:

Therefore, in eventbus, if Inter thread switching is required, it is mainly thrown to different task queues to realize inter thread switching.

Judging from the task queue, switching targets include main thread poster, backgroundposter and asyncposter.

Let's first look at the design of task queue:

Task queue

Because eventbus cannot judge which tasks will be parallel, it adopts the queue design. Multithreaded tasks (events of eventbus) will enter the queue first and then process the work tasks in the queue. This is a typical production consumption scenario. The main thread poster, backgroundposter, and asyncposter are all different implementations of the task queue.

Main thread Poster

Mainthreadposter, which is responsible for processing the main thread, is a subclass of handler:

It can be seen from the source code that this poster is actually a handler. The message queue of which thread it uses determines which thread it can communicate with. Let's confirm:

Therefore, eventbus extends a handler as the handler of the main thread and realizes multi-threaded switching through the handler message mechanism.

Of course, this handler has another layer of queue.

Backgroundposter and asyncposter

Backgroundposter and asyncposter actually use the thread pool of eventbus. The default is a cache thread pool:

Therefore, both backgroundposter and asyncposter delegate tasks to the thread pool for processing, so as to realize multi-threaded switching.

However, there are some differences between backgroundposter and asyncposter. We know that in the newcachedthreadpool, the maximum number of threads is the maximum value of integer, which is equivalent to no upper limit, so you can start as many threads as possible. Asyncposter does this. Enqueu and run are not synchronized, and new threads are started for each event.

In backgroundposter, threads can be reused as much as possible. The main method is to wait for 1 second when running:

Because of the one second pending wait, synchronized (this) is required to ensure thread safety during enqueue and run.

In addition, there is also a very important usage here, which is the synchronousqueue in executors. Newcachedthreadpool():

This synchronousqueue is also used in okhttp:

Different from ordinary queues, synchronous queue is not a thread such as data, but a thread such as data. In this way, each time data is transferred to synchronous queue, it will be immediately handed over to a thread for execution, which can improve the speed of data processing.

In general, eventbus still uses thread pool to realize working threads, and uses handler mechanism to notify the main thread. The difference is that it uses the queue method to manage all cross thread requests, and it uses the synchronous queue blocking queue to assist in thread switching.

Multithreading switching of rxjava

In fact, in terms of multithreading management, rxjava's thread management ability is very amazing. The main concept of rxjava is workflow, which can define a sequence of workflows on a thread type:

The workflow construction process is actually quite complicated. However, if we only look at the thread operation part, the process is very clear. Let's trace the source code of subscribeon (rxjava2):

Then, enter the flowablesubscribeon class

This subscribeonsubscriber is an internal class:

The worker is actually the thread parameter we entered, such as schedulers. IO (), which is defined as follows:

The IO here will eventually point to a scheduler, such as ioscheduler:

In this way, the specific tasks in scheculler are handed over to a thread pool.

What needs to be specified is that the Android main thread (AndroidSchedulers.mainThread) in RxJava actually uses the Handler mechanism:

This handlerscheduler actually implements the internal classes of scheduler and scheduler.worker.

In general, rxjava's multi-threaded switching actually uses the internal class scheculler. Worker to hand over tasks to scheculler's worker, who implements different thread pools according to the defined threads. In fact, it is still handed over to the thread pool for processing. As for the main thread, rxjava also uses the handler mechanism.

summary

To sum up, basically, multithreading switching in Android mainly uses runnable and callable to define work content, uses thread pool to realize asynchronous parallelism, and uses handler mechanism to notify the main thread. In some scenarios, future interface callback will be used as needed, and synchronousqueue will be used to block the queue.

The above is the whole content of this article. I hope it will be helpful to your study, and I hope you can support programming tips.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>