Android processes and threads (understand > use > common interview questions)

Keywords: Java Linux Android Design Pattern Interview

🔥 concept

💥 process

  • The basic unit of program operation.

  • The basic unit of system resource allocation and scheduling.

  • Has its own independent address space.

  • Multiple processes can execute concurrently.

  • Thread container

💥 thread

  • The smallest unit of program execution.

  • The basic unit of CPU scheduling and dispatch.

  • There is no independent address space, and multiple threads share the address space.

  • Multiple threads can execute concurrently, and one thread can create and revoke another thread.

💥 Difference between process and thread

  • Address space: threads of the same process share the address space of the process, while processes have independent address space.

  • Resource ownership: threads in the same process share the resources of the process, such as memory, I/O, CPU, etc., but the resources between processes are independent.

  • After a process crashes, it will not affect other processes in protected mode, but a thread crashes and the whole process dies. Therefore, using multiple processes can ensure the normal operation of other modules.

  • Process switching consumes a lot of resources and is inefficient. Therefore, when it comes to frequent switching, using threads is better than processes. Similarly, if concurrent operations that require simultaneous and shared variables are required, only threads can be used, not processes.

  • Execution process: each independent process has an entry for program operation, sequential execution sequence and program entry. However, threads cannot be executed independently. They must be stored in the application, and the application provides multiple thread execution control.

  • Can be executed concurrently.

  • A program has at least one process, and a process has at least one thread.

The system can be regarded as a factory. A process is a workshop, multiple processes are multiple workshops, threads are pipelines, multiple processes are multiple workshops, and multiple threads are multiple pipelines.

🔥 Processes in Android

💥 process

When an application component starts and no other components of the application are running, the Android system starts a new Linux Process for the application and uses a single execution thread. By default, all components of the same application run in the same process and thread (called the "main" thread).

💥 Level of process (lifecycle)

🌀 Foreground process

It indicates that the user is interacting with the process, and the priority is the highest. Android system - marks a process as a foreground process according to the following conditions:

  • There is an Activity and the * * onResume() * * method is being executed (the user is interacting with it).

  • There is a Service and one of the methods (onCreate(), onStart(), onDestroy()) is being executed.

  • There is a BroadcastReceiver and the onReceive() method is being executed.

🌀 Visible process

It shows that although the process does not hold any foreground components, it can still affect the user's visible interface. The android system marks a process as a visible process according to the following conditions:

  • There is an activity and it is not interacting, but it is still visible (its onPause() method has been called). For example, when an activity starts a dialog, the activity is blocked by the dialog.

  • There is a * * Service executing the Service. Startforegroup() * * method.

  • The managed system is used for services with specific functions known to users, such as dynamic wallpaper, input method services, etc.

🌀 Service process

Hold the Service that has been started using the startService() method. Although these processes are not directly visible to users, they are generally doing things that users care about (such as background network data upload or download)

🌀 Cached process

Cache process is a process that is not needed at present. Therefore, when memory and other resources are needed in other places, the system can terminate it as needed.

  • Holds one or more invisible activities (onStop() method called). Usually, there are many background processes. When the memory is insufficient, all background processes will recycle the processes that have not been used for the longest time according to the LRU (recently used) rule.

When deciding how to classify a process, the system makes a decision based on the most important level found in all components of the current activity in the process.

The priority of a process can also be increased based on other dependencies of the process on it.

💥 Multi process

By default, all components of the same application run in the same process, and most applications should not change this. However, if you find that you need to control which process a component belongs to, you can do it in < Application >.

The manifest entries of each type of component element (< activity >, < Service >, < receiver >, and < provider >) support the android:process attribute, which can specify the process in which the component should run.

The < Application > element also supports the android:process attribute.

An unusual basic feature of Android is that the life cycle of the application process is not directly controlled by the application itself. Instead, it is determined by the system knowing the combination of running application parts, the importance of these things to users, and the total amount of memory available in the system.

The default process is the main process. Other processes are generally child processes.

For example, the wechat we use has so many functions that it certainly does not operate in a default process. Using multiple processes, even if a function crashes due to thread problems, a process will crash, and other functions other than the process can be used normally.

🌀 Multiple applications generated by multiple processes

If any of the four registered components uses multiple processes, a new Application object will be created when running the component. In the case of multiple processes creating applications repeatedly, you only need to judge the current process in this class.

com.scc.demo(12095):com.scc.demo process name; 12095 process id

Code implementation: AndroidMainfest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.scc.demo">
    <application
        android:name=".SccApp"
        ...
        android:theme="@style/Theme.Demo">
        <activity android:name=".actvitiy.MainActivity"
            >
            ...
        </activity>

        <activity android:name=".actvitiy.TouchActivity"
            android:process="com.scc.touch.wudi"/>
        <activity android:name=".actvitiy.ViewActivity"
            android:process=":view"/>
        ...
    </application>

</manifest>

Compare whether to initialize according to the default process name and the current process name.

public class SccApp extends Application {
    @RequiresApi(api = Build.VERSION_CODES.P)
    @Override
    public void onCreate() {
        super.onCreate();
        getProcessName(BuildConfig.APPLICATION_ID);
    }
    public void getProcessName(String processName){
        ActivityManager activityManager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> processInfos = activityManager.getRunningAppProcesses();
        if(processInfos!=null)
        {
            for(ActivityManager.RunningAppProcessInfo processInfo:processInfos){
                MLog.e(processInfo.processName);
                if(processName.equals(processInfo.processName)){
                    init();
                }
            }
        }
    }
    //initialization
    private void init(){
        //
        CrashReport.initCrashReport(getApplicationContext(), "Applied for at the time of registration APPID", false);
    }
}

💥 Interprocess communication

  • Bundle

  • File sharing

  • AIDL

  • Messenger

  • Content Provider

  • Socket

This content is too much. I will describe it in detail in the later article.

🔥 Threads in Android

There are two types of threads:

  • UI/Main Thread

  • Worker thread

A thread is always started by another thread, so there is always a special thread called the main thread. It is the first thread that the application starts and executes. Each time a new worker thread is started, a separate line is separated from the main thread.

💥 UI/Main Thread

When the application is started, the system will create an execution thread for the application, which is called "main". This thread is important because it is responsible for sending events to the appropriate user interface widgets, including drawing events. Threads that interact with the Android UI toolkit (components from the Android.widget and Android.view packages).

Therefore, the main thread is sometimes referred to as the UI thread. However, in special cases, the main thread of an application may not be its UI thread.

When using thread annotation, note: the build tool treats @MainThread and @UiThread annotations as interchangeable, so you can call @UiThread from the @MainThread method, and vice versa. However, when the system application has multiple views on different threads, the UI thread may be different from the main thread. Therefore, you should use @ mainthread for @ uitthread

All components running in the same process are instantiated in the UI thread.

In addition, the Android UI toolkit is not thread safe. Therefore, you cannot operate the UI from a worker thread - you must perform all operations on the user interface from the UI thread. Therefore, Android's single thread model has only two rules:

  • Do not block UI threads;

  • Do not access the UI on a non UI thread.

🌀 Blocking UI threads

If everything happens in the UI thread, performing long operations, such as network access or database queries, will block the entire UI.

Causes of ANR:

  • Activity does not respond for more than 5 seconds;

  • The BroadcastReceiver is not responding for more than 10 seconds.

🌀 Worker Thread operation UI

@Override
protected void onCreate(@Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_thread);
    //Worker thread
    new Thread(new Runnable() {
        @Override
        public void run() {
            //Action UI thread
            Toast.makeText(ThreadActivity.this,"I am Worker Thread",Toast.LENGTH_SHORT).show();
        }
    }).start();
}

Direct error reporting after operation:

2021-10-12 14:47:47.495 4122-4247/com.scc.demo E/AndroidRuntime: FATAL EXCEPTION: Thread-7
    Process: com.scc.demo, PID: 4122
    java.lang.RuntimeException: Can't toast on a thread that has not called Looper.prepare()
        at android.widget.Toast$TN.<init>(Toast.java:895)
        at android.widget.Toast.<init>(Toast.java:205)
        at android.widget.Toast.makeText(Toast.java:597)
        at android.widget.Toast.makeText(Toast.java:566)
        at com.scc.demo.actvitiy.ThreadActivity$1.run(ThreadActivity.java:18)
        at java.lang.Thread.run(Thread.java:919)

💥 Worker thread

The main thread cannot be blocked, but some time-consuming operations (such as loading pictures, network requests, etc.) are not immediate, and the corresponding operations can be executed through the worker thread.

Note that you cannot update the UI from any thread other than the UI thread or the "main" thread.

To solve this problem, Android provides several ways to access UI threads from other threads:

  • Activity.runOnUiThread(Runnable)

  • View.post(Runnable)

  • View.postDelayed(Runnable, long)

🌀 Example: child thread accessing UI thread

public class ThreadActivity extends ActivityBase{
    TextView tvName;
    @Override
    protected void onCreate(@Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_thread);
        tvName = findViewById(R.id.tv_name);
        tvName.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                csThread();
                startThread();
            }
        });
    }
    private void csThread(){
        //Worker thread
        new Thread(new Runnable() {
            @Override
            public void run() {
                //Write this directly and report an error
                tvName.setText("I am Worker Thread---It's hard to travel! It's hard to travel!");
//                ------Powerful split line------
//                The following methods are OK
                //The first: Activity.runOnUiThread(Runnable)
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        tvName.setText("I am Worker Thread---It's hard to travel! It's hard to travel!");
                        Toast.makeText(ThreadActivity.this,"I am Worker Thread",Toast.LENGTH_SHORT).show();
                    }
                });

                //Second: View.post(Runnable)
                tvName.post(new Runnable() {
                    @Override
                    public void run() {
                        tvName.setText("I am Worker Thread---It's hard to travel! It's hard to travel!");
                        Toast.makeText(ThreadActivity.this,"I am Worker Thread",Toast.LENGTH_SHORT).show();
                    }
                });

                //The third type: View.postDelayed(Runnable, long)
                tvName.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        tvName.setText("I am Worker Thread---It's hard to travel! It's hard to travel!");
                        Toast.makeText(ThreadActivity.this,"I am Worker Thread",Toast.LENGTH_SHORT).show();
                    }
                },1000);

                //The fourth type: Handler (there are source codes below, which are all based on Handler)
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        tvName.setText("I am Worker Thread---It's hard to travel! It's hard to travel!");
                        Toast.makeText(ThreadActivity.this,"I am Worker Thread",Toast.LENGTH_SHORT).show();
                    }
                });
            }
        }).start();
    }
}

The sub thread directly operates on the main thread, and the error message is:

Theoretically, you should take the error message of 3.1.2 Worker Thread when operating the UI. Since all can be solved in this way, give one more example.

2021-10-12 16:02:51.754 8635-8676/com.scc.demo E/AndroidRuntime: FATAL EXCEPTION: Thread-2
    Process: com.scc.demo, PID: 8635
    android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
        at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:8798)
        at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1606)
        at android.view.View.requestLayout(View.java:25390)
        ...
        at android.widget.TextView.checkForRelayout(TextView.java:9719)
        at android.widget.TextView.setText(TextView.java:6311)
        ...
        at com.scc.demo.actvitiy.ThreadActivity$2.run(ThreadActivity.java:31)
        at java.lang.Thread.run(Thread.java:923)

🌀 Several methods source code

    //Activity.runOnUiThread(Runnable)
    public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }

    //View.post(Runnable)
    public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }
        getRunQueue().post(action);
        return true;
    }

    //View.postDelayed(Runnable, long)
    public boolean postDelayed(Runnable action, long delayMillis) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.postDelayed(action, delayMillis);
        }

        getRunQueue().postDelayed(action, delayMillis);
        return true;
    } 

You will find that they all use Handler. Therefore, in the example where the child thread accesses the UI thread, you can use new Handler() to update the UI.

💥 Status of the thread

  • New: new status. New comes out and start has not been called yet.

  • Runnable: runnable. Call start to enter the runnable state. It may or may not be running, depending on the scheduling of the operating system.

  • Blocked: blocked status, blocked by lock and temporarily inactive. The blocked status is the status of thread blocking when entering the method or code block modified by synchronized keyword (obtaining lock).

  • Waiting: waiting state, inactive, no code running, waiting for thread scheduler scheduling, wait sleep.

  • Timed Waiting: wait for a timeout and return automatically at the specified time.

  • Terminated: termination status, including normal termination and abnormal termination.

💥 Three ways to start threads

  • 1: Inherit Thread and override run method.

  • 2: Implement Runnable to override the run method.

  • 3: Implement Callable to override the call method.

    private void startThread(){
        //The first method: inherit Thread and override run method
        new MyThread().start();
        //The second method is to implement Runnable and override the run method
        new Thread(new MyRunanble()).start();
        //The third method is to implement Callable and override the call method
        FutureTask<Integer> ft = new FutureTask<Integer>(new MyCallable());
        new Thread(ft).start();

    }
    class MyThread extends Thread{
        @Override
        public void run() {
            MLog.e(this.getClass().getName());
        }
    }
    class MyRunanble implements Runnable{

        @Override
        public void run() {
            MLog.e(this.getClass().getName());
        }
    }
    class MyCallable implements Callable {
        @Override
        public Object call() throws Exception {
            MLog.e(this.getClass().getName());
            return null;
        }
    }

🌀 Summary

Callable is similar to Runnable, but more powerful, as shown in:

  • You can provide a return value after the task ends, but Runnable cannot.

  • The call method can throw exceptions, but the run method of Runnable cannot

  • You can listen to the result of the call method called by the target thread through the wealth object obtained by running Callable to obtain the return value (wealth. Get(), which will block after calling until the return value is obtained).

🔥 Common interview questions

💥 The difference between run() and start() methods

  • run(): the method is only the main method of the thread. Like ordinary methods, it will not create a new thread.

  • start(): a new thread will be started only when the start() method is called. The new thread will call the run() method and the thread will start execution.

💥 wait,notify,notifyAll

  • wait(): release the obj lock, causing the current thread to wait, and directly call the notify() or notifyAll() methods of this object from other threads.

  • notify(): wakes up a single thread waiting on this object monitor

  • notifyAll(): notifies all threads waiting for the contention resource

Note: when calling wait() or notify()/notifyAll() methods, be sure to put them in the synchronized(obj) code, otherwise an error java.lang.IllegalMonitorStateException will be reported. When obj.notify/notifyAll is invoked, the calling thread still holds the obj lock, so the waiting thread is waken up, but it is still unable to get the obj lock until the calling thread exits the synchronized block. After the obj lock is released, other waiting threads will have the chance to get the lock to continue execution.

💥 join,sleep,wait

  • join(): the method releases the object lock while waiting.

  • sleep(): the method does not release the object lock during sleep,

  • wait(): release the object lock

💥 Thread blocking

  • 1: The thread executes the Thread.sleep(int millsecond) method, abandons the CPU, sleeps for a period of time, and resumes execution after a period of time;

  • 2: The thread executes a section of synchronization code, but cannot obtain the relevant synchronization lock. It can only enter the blocking state. Execution can be resumed only after the synchronization lock is obtained;

  • 3: The thread executes the wait() method of an object, directly enters the blocking state, and waits for other threads to execute notify()/notifyAll();

  • 4: When a thread performs some IO operations, it enters the blocking state because it waits for related resources, such as System.in, but does not receive keyboard input, it enters the blocking state.

  • 5: Thread comity, the Thread.yield() method pauses the thread object currently executing and gives the execution opportunity to the thread with the same or higher priority, but it will not make the thread enter the blocking state. The thread is still in the executable state and may share the CPU time again at any time.

  • 6: Thread self closing, join() method. When the current thread calls the join() method of another thread, the current thread enters the blocking state until the other thread runs, and the current thread changes from blocking to ready state.

  • 7: The thread executes suspend() to make the thread enter the blocking state. The resume() method must be called to make the thread enter the executable state again.

💥 Thread interrupt

interrupt() is used to interrupt, but calling the interrupt() method only passes the interrupt request message, which does not mean that the target thread should be stopped immediately. Then wake it up by throwing InterruptedException.

public class Thread {
    // Interrupt current thread
    public void interrupt();
    // Determine whether the current thread is interrupted
    public boolen isInterrupt();
    // Clears the interrupt status of the current thread and returns the previous value
    public static boolen interrupted();
}

💥 Thread pool ThreadPoolExecutor

Working principle of thread pool: thread pool can reduce the number of threads created and destroyed, so as to reduce the consumption of system resources.

When a task is submitted to the thread pool:

  • 1: First, judge whether the threads in the core thread pool are full. If not, create a core thread to execute the task, otherwise enter the next step.

  • 2: Judge whether the work queue is full. If not, join the work queue. Otherwise, execute the next step.

  • 3: Judge whether the number of threads reaches the maximum value. If not, create a non core thread to execute the task. Otherwise, execute the saturation strategy and throw an exception by default.

💥 Type of thread pool

FixedThreadPool: a reusable thread pool with a fixed number of threads. There are only core threads, no non core threads, and the core threads will not be recycled. When there are tasks, idle core threads will be executed by the core threads, and if not, they will join the queue.

Singlethreadexecution: single thread thread pool. There is only one core thread and no non core thread. When a task arrives, if there is no running thread, a thread will be created for execution. If it is running, it will join the queue and wait. It can ensure that all tasks are executed in order in one thread. The difference from FixedThreadPool is only quantity.

CachedThreadPool: a thread pool created on demand. There are no core threads, and non core threads have Integer.MAX_VALUE. Each task submitted will be executed by an idle thread if there is one. If there is no idle thread, a new thread will be created for execution. It is applicable to a large number of tasks that need immediate processing and take a short time.

ScheduledThreadPoolExecutor: inherited from ThreadPoolExecutor. It is used to delay or periodically execute tasks. The number of core threads is fixed, and the total number of threads is Integer.MAX_VALUE.

💥 How to ensure thread safety

Thread safety is embodied in:

  • Atomicity: provides mutually exclusive access. Only one line and to data can be operated at a time.

    JDK provides many atomic classes, such as AtomicInteger\AtomicBoolean\AtomicLong, which complete atomicity through CAS.

    There are two types of locks provided by JDK:

    • synchronized relies on the JVM to implement the lock. Within the scope of the keyword object, only one thread can operate at a time.
    • The other is LOCK, which is a code level LOCK provided by JDK and depends on CPU instructions. The representative is ReentrantLock.
  • Visibility: the modification of main memory by one thread is seen by other threads in time.

    The JVM provides synchronized and volatile. The visibility of volatile is realized through memory barrier and prohibition of reordering. Volatile will add a store barrier instruction after the write operation to refresh the shared variable values in local memory to main memory; During the read operation, a load instruction will be added before the read operation to read the shared variables from memory.

  • Ordering: the instructions are not reordered by the compiler.

    Order can be ensured through volatile, synchronized and Lock.

💥 volatile, synchronized, Lock, ReentrantLock differences

  • volatile: solves the visibility of variables among multiple threads, but cannot guarantee atomicity. It can only be used to modify variables without blocking. volatile can shield the rearrangement of compiled instructions, and will not put the instructions behind it in front of the memory barrier, nor will it put the previous instructions behind the memory barrier. Singleton mode for parallel computing. volatile stipulates that the CPU must read data from memory every time, not from the CPU cache, which ensures that the latest value is always obtained by multi threads in multi CPU computing.

  • Synchronized: mutually exclusive lock, mutually exclusive operation, concurrent thread coming, serially obtaining lock and serially executing code. The solution is the synchronization of accessing shared resources between multiple threads, which can ensure atomicity or indirectly ensure visibility, because it will synchronize the data in private memory and public memory. It can be used to decorate methods and code blocks. Blocking will occur. When the synchronized exception occurs, the lock held by the thread will be released automatically, so it will not cause deadlock. Unfair locks compete for resources every time.

  • Lock: it is an interface that allows threads waiting for locks to respond to interrupts. When an exception occurs, if you do not actively release the lock through unLock(), it may cause deadlock. Therefore, when using lock, you need to release the lock in the finally block.

  • ReentrantLoc: reentrant lock. The allocation mechanism of lock is thread based allocation, not method call based allocation. ReentrantLock has a tryLock method. If the lock is held by other threads, false is returned to avoid deadlock. Locking the code will have smaller particles, save resources and improve code performance. ReentrantLock can realize fair lock and unfair lock. Fair lock is to obtain resources first come first. ReentrantReadWriteLock is used when there are more reads and less writes, and there is no need for mutual exclusion.

💥 Why can't Thread stop the Thread with the stop method

It can be seen from the official documents that calling the Thread.stop() method is unsafe because the following two things will happen when calling the Thread.stop() method:

  • 1: Throw a ThreadDeath exception immediately. It is possible to throw a ThreadDeath Error at any point in the run() method of the thread, including in the catch or finally statement.

  • 2: Release all locks held by the thread. After calling thread.stop(), all the locks held by the thread are released suddenly, and the protected data may be inconsistent. When other threads use these damaged data, they may cause some strange application errors.

💥 Synchronization method in java

Why do I need to synchronize? In multithread concurrency control, when multiple threads operate a shared resource at the same time, if no synchronization mechanism is adopted, the data will be inaccurate. Therefore, it is necessary to add a synchronization lock to ensure that the thread is called by other threads before the operation is completed, so as to ensure the uniqueness and accuracy of the variable.

  • Synchronized modifies a synchronized code block or method

    Since each object in java has a built-in lock, when you modify a method with this keyword, the built-in lock will protect the whole method. Before calling this method, you need to obtain the built-in lock, otherwise it will be in a vaginal state.

  • volatile modifier variable

    Ensure the visibility of variables between threads. Each time a thread accesses a variable decorated with volatile, it reads it from memory instead of cache, so that the variables accessed by each thread are the same. And use memory barriers.

  • ReentrantLock reentrant lock. Common methods:

    • ReentrantLock(): create a ReentrantLock instance
    • lock(): get lock
    • unlock(): release the lock
  • Thread synchronization is realized by using the local variable ThreadLocal. Each thread will save a copy of the variable. The copies are independent of each other, so that each thread can modify its own copy at will without affecting other threads. Common methods:

    • ThreadLocal(): create a thread local variable;
    • get(): returns the current thread copy variable of this thread part;
    • initialValue(): returns the initial value of the current thread of the thread's local variable;
    • set(T value): set the value in the current thread copy of this thread variable to value
  • Common methods for using atomic variables, such as AtomicInteger:

    • AtomicInteger(int value): creates an AtomicInteger integer with a given initial value;
    • addAndGet(int data): adds the given value to the current value atomically
  • Thread synchronization using blocking queue linkedblockingqueue < E >

💥 Two processes require writing or reading at the same time. Can it be realized? How to prevent process synchronization?

It can be implemented. For example, there is no problem for both processes to read calendar process data, but there should be conflicts when writing at the same time.

Shared memory can be used to share data between processes.

Posted by pinacoladaxb on Mon, 18 Oct 2021 14:59:13 -0700