Android Multithreading Series

  • Blocking queues for Android multithreading
  • The common thread form of Android multithreading
  • The correct use of thread pools for Android multithreading

Processes and threads in Android

  • An application in Android generally corresponds to a process, the situation of multi-process can refer to several basic issues of Android multi-process communication
  • Android is more common in the case of multithreading, an application generally has more than one thread including the UI thread. In Android, network access must be done in child threads, while UI updates can only be done in the UI thread.
  • Common network request libraries, such as OkHttp and Volly, have wrapped thread pools for us, so it is not intuitive for us to feel the process of creating and switching threads when making network requests.
  • Threads are a valuable resource, and to avoid frequent thread creation and destruction, thread pools are generally recommended to manage threads.

Thread state

Threads can be in six different states: New, Runnable, Blocked, Waiting, Timed wait, and Terminated.

  • Newly created (New) : Threads that have been created but not started (that have not called the start method) are in this state
  • Runnable: Once the start method is called, the thread is in this state. Note that the thread may be executing or waiting for the CPU to allocate time for execution
  • Blocked: A thread is Blocked by a lock and is waiting to acquire an exclusive lock. The thread enters this state while the program is waiting to enter the synchronization zone
  • Waiting: Threads in this state are not allocated CPU time. They wait to be awakened explicitly by another thread. Calling the following methods puts the thread into this state:
    • Object.wait() method with no Timeout parameter
    • Thread.join() method with no Timeout parameter
  • Timed Waiting: Unlike a Waiting state, a thread in this state does not have to wait for another thread to wake up, but is awakened by the system after a certain amount of time. Calling the following methods puts the thread into this state:
    • Thread.sleep () method
    • The object.wait () method with the Timeout parameter set
    • Thread.join() method with Timeout parameter set
  • Terminated state: indicates that the thread is Terminated. There are two ways to cause a thread to terminate:
    • The run method of the thread completes and exits normally
    • The run method was terminated because of an exception that was not caught

Create a thread

Threads are created in one of the following ways: by inheriting the Thread class; Implement Runnable interface; Implement Callable interface

  • Inherit the Thread class and override the run method
public class TestThread extends Thread {
    @Override
    public void run() {
        System.out.println("Hello World"); } public static void main(String[] args) { Thread mThread = new TestThread(); mThread.start(); }}Copy the code
  • Implement Runnable interface, and implement the run method
public class TestRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Hello World"); } public static void main(String[] args) { TestRunnable mTestRunnable = new TestRunnable(); Thread mThread = new Thread(mTestRunnable); mThread.start(); }}Copy the code
  • Implement Callable interface, rewrite call method
    • Callable can provide a return value upon task acceptance while Runnable cannot
    • The Call method of Callable can throw an exception, but the run method of Runnable cannot
    • Call Callable to get a Future object that represents the result of the computation, and the Future’s get method to get the result of the asynchronous computation, but the current thread blocks.
public class TestCallable { public static class MyTestCallable implements Callable<String> { @Override public String Call () throws Exception {// The call method can provide a return value, but Runnable cannotreturn "Hello World"; } } public static void main(String[] args) { MyTestCallable myTestCallable = new MyTestCallable(); ExecutorService = new ThreadPoolExecutor(1,1,0L, timeunit.seconds, new LinkedBlockingQueue<Runnable>(10)); Future Future = executorService.submit(myTestCallable); Try {// Wait for the thread to finish, the future.get() method will block the current thread system.out.println (future.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }}}Copy the code
  • These three methods are common for creating threads. The method of implementing the Runnable interface is recommended.

Thread the interrupt

  • When a thread calls the interrupt method, the thread’s interrupt flag is set to true
  • Use thread.currentThread ().isinterrupted () to determine whether a Thread should be interrupted
  • You can reset the interrupt flag bit by calling Thread.interrupted() (set to false)
  • If a thread is blocked and finds that the interrupt flag bit is true when it checks the interrupt flag bit, it throws InterruptedException at the blocking method and resets the interrupt flag bit to false before throwing the exception
  • Do not catch InterruptedException underneath your code and leave it unchecked

Several methods of synchronization

The synchronization methods are volatile, synchronized, and ReentrantLock

The volatile keyword
  • The key to multithreaded safety is the visibility of the volatile keyword, but it must meet certain conditions to ensure thread safety.
  • When using the volatile keyword for multithreaded safety, note that volatile does not guarantee atomicity. It cannot be used for increment, decrement, and other operations, and it cannot be used for invariants
public class VolatileTest {
    private volatile int lower,upper;

    public int getLower() {
        return lower;
    }

    public void setLower(int value) {
        if (value > upper) {
            throw new IllegalArgumentException();
        }
        this.lower = value;
    }

    public int getUpper() {
        return upper;
    }

    public void setUpper(int value) {
        if(value < lower) { throw new IllegalArgumentException(); } this.upper = value; }}Copy the code
  • In the example above, if the initial value is (0,5), thread A calls setLower(4) and thread B calls setUpper(3), the result would obviously be (4,3)
  • Common scenarios for volatile use are as a status flag and the DCL singleton pattern
Synchronized keyword and ReentrantLock
  • The synchronized keyword is common and can be used to synchronize a method or a code block. The synchronized method is recommended because the security of the synchronized code block is not high.
  • Compared with synchronized, ReentrantLock provides some unique features: multiple conditions can be bound to unlock, fair lock can be realized, and the waiting time for lock acquisition can be set.
public class ReentrantLockTest {
    private Lock mLock = new ReentrantLock();
    //trueTo implement fair lock <! --private Lock mLock = new ReentrantLock(true); --> private Condition condition; private void thread1() throws InterruptedException{ mLock.lock(); try { condition = mLock.newCondition(); condition.await(); System.out.println(Thread1: "Hello World");
        }finally {
            mLock.unlock();
        }
    }

    private void thread2() throws InterruptedException{
        mLock.lock();
        try {
            System.out.println("Thread2: Hello World"); condition.signalAll(); }finally { mLock.unlock(); }}}Copy the code
  • A ReentrantLock has multiple conditions associated with it, and calling the await method of the Condition causes the current thread to enter the Condition’s wait set and block until another thread calls the signalAll method of the same Condition to activate all threads that are blocked because of the Condition

  • Synchronized synchronization methods and some of the classes provided by the java.util.concurrent package are used more often for thread synchronization in general

How do I terminate a thread safely

Although we generally use thread pools to manage threads rather than explicitly create them, as part of our knowledge of threads, we need to know how to safely terminate a thread.

There are generally two ways to safely terminate a thread: interrupts and flag bits

(1) Use interrupts to terminate threads

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        while(! Thread.currentThread().isInterrupted()) { //dosomething } } }); // When we call threadInterrupt, the Thread exits the loop and stops. thread.interrupt();Copy the code

(2) Pass the flag bit

Private static class MyRunnable implements Runnable {// The thread flag is not volatile. Private volatile Boolean on =true;

    @Override
    public void run() {
        while (on) {
           //do something 
        }
    }
    
    public void cancel() {
        on = false; MyRunnable MyRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); // Terminate the thread myrunnable.cancel ();Copy the code

Welcome to pay attention to my wechat public number, looking forward to learning, communicating and growing together with you!Copy the code