• How threads are used

1. Create background threads to execute tasks, which most people (including me) would choose
// New Thread(new Runnable())Copy the code

Start () is then used to start the thread. Start () executes the start0() native method, and the virtual machine calls the run method. If Runnable is present, the run() implementation of Runnable is called; otherwise, the run() method of Thread is executed.

ThreadFactory: Factory mode, easy to do some uniform initialization operations:
ThreadFactory factory=new ThreadFactory(){
    @Override
    public Thread newThread(Runnable r){
        return new Thread(r);
    }
}

Runnable runnable =new Runnable(){
    / / ` ` `
}

Thread thread=factory.newThread(runnable);
thread.start();
Thread thread1=factory.newThread(runnable);
thread1.start();
Copy the code
3. Executor: The most commonly used but rarely used method:
Runnable runnable =new Runnable(){
    @Override
    public void run(){
        //```
    }
}

Executor executor=Executors.newCachedThreadPool();
executor.executor(runnable);
executor.executor(runnable);
(ExecutorService)executor.shutdown();
Copy the code

For me, at least, it seems to be rare, so why is it the most common? AsyncTask, Cursor, and Rxjava actually use executors for thread operations.

You can look at Executor, which is just an interface to specify how threads work through execute()

public interface Executor {
    void execute(Runnable command);
}

Copy the code

Extensions for Executor: ExecutorServive/Executors Executors. NewCachedThreadPool () returns another ExecutorSevice extends Executor, which makes some extensions to Executor, focuses on shutdown() (conservative end) /shotdownNow() (immediate end), and future-related submit(), which will be said later. NewCachedThreadPool () creates a cached thread pool for automatic thread creation, caching, and recycling. There are a few other methods: newSingleThreadExecutor() single-threaded, less useful. NewFixedThreadPool () specifies a thread pool of fixed size. For example, you need to create batch tasks. NewScheduledThreadPool () specifies a schedule, a delay, or a specified execution time. If you want to customize the thread pool, you can use these methods to directly new ThreadPoolExecutor during app initialization. End when the thread completes: You can simply add the task and then execute shutdown(). The end of a thread, written later, starts with basic start/end.

Several arguments to ThreadPoolExecutor()

    public static ExecutorService newCachedThreadPool(a) {
        return new ThreadPoolExecutor(
        0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,
        new SynchronousQueue<Runnable>());
    }
   
    public ThreadPoolExecutor(
    intCorePoolSize,// The size of the initial thread pool/how many threads are recycled after the creation thread completes its executionintMaximumPoolSize,// thread onlinelongTimeUnit unit, BlockingQueue<Runnable> workQueue,// Block ThreadFactory ThreadFactory, RejectedExecutionHandler handler) {
    / / ` ` `
    }

Copy the code

MaximumPoolSize maximumPoolSize maximumPoolSize maximumPoolSize maximumPoolSize maximumPoolSize maximumPoolSize maximumPoolSize maximumPoolSize maximumPoolSize Now learned: first certainly not for a core to a thread, after all, a CPU runs N multiple threads, which can be so just a core a. It is possible to ensure that the code can be equally scheduled by CPU on different machines. For example, if the code is single-core, it will create two threads. If the code is 8-core, it will create 16 threads. Otherwise writing 8 threads would be too slow for a single core machine and too few for 8 core machines.

4. Callable can simply be described as a background thread with a return value. Android is rarely used, so simply record it
     Callable callable = new Callable<String>() {
            @Override
            public String call(a) throws Exception {
                try {
                    Thread.sleep(1500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return "Find the bottle."; }}; ExecutorService executor = Executors.newCachedThreadPool(); Future<String> future = executor.submit(callable);try {
            String result = future.get();
        } catch (ExecutionException | InterruptedException e) {
            e.printStackTrace();
        }
Copy the code

Doesn’t it seem like too much trouble? There’s even a little bit to work with? But the Future blocks the thread, and if it is used in the main thread, you need to keep checking to see if the background execution is finished

     while (true) {
            if (future.isDone()){
                try {
                    String result = future.get();
                } catch (ExecutionException | InterruptedException e) {
                    e.printStackTrace();
                }
                try{
                    Thread.sleep(1000);// Simulate the main thread task, take a look in a second
                } catch(InterruptedException e) { e.printStackTrace(); }}}Copy the code

Ok, that’s pretty much the common way threads are used. In use, we often encounter the problem of multiple threads operating the same resource. For example, when A transfers money to B, B and C transfer money to A at the same time. So what should we do? This brings us to the thread safety issue of section 2.

  • Thread safety/synchronization issues:

1. Causes:

The time slice mechanism used by the OPERATING system for the CPU may cause data errors when a certain code thread is suspended during execution and other threads continue to execute the same code/operate on the same data (resource). Eg:

class Test{
    
    private int x=0;
    private int y=0;

    private void ifEquals(int val){
        x=val;
        y=val
        if(x! =y){
            System.out.println("x: "+x+",y: "+y); }}public void testThread(a){
        new Thread(){
            public void run(a){
                for(int i=0; i<1 _000_000_000; i++){ ifEquals(i); } } }.start();new Thread(){
            public void run(a){
                for(int i=0; i<1 _000_000_000;i++){
                    ifEquals(i);
                }
            }
            }.start();
        }

}
Copy the code

How could this be different? The reason for this is actually simple, as outlined above. When testThread is executed, both threads operate on the ifEquals method at the same time:

  1. Thread 1 is switched when x=val=10 and y=val has not been assigned by thread 1
  2. Thread 2 executes the code when x=100, y=100, and then the thread switches again
  3. Thread 1 executes y=10, at which point x is already the 100 changed by thread 2, resulting in x! = y.
2. Synchronized

So knowing the reason, the solution becomes simple, the two steps of x and y should be one operation. Or one ifEquals method becomes one operation. So JAVA provides the synchronized keyword, which is added to the ifEquals method to make it atomic. A Monitor is added to the method so that it will not be released while thread 1 is executing. Even if thread 2 is switched, when thread 2 accesses the method, it will queue up and not execute the method because the Monitor is not released.

Now that the Synchronized key is added to ifEquals, does the code above add the following delVal() method to the thread to call x, y values? Will remain. So, what looks like the protection of methods is actually the protection of resources, like in the example above, what we really want is not to protect the ifEquals method, but to protect the resources of X and Y.

    private void delVal(int val){
        x=val-1;
        y=val-1; } String name;private Synchronized void setName(String val){
        name=val;
    }

Copy the code
3. About the Monitor

Another problem is that when protecting x/y, it is also necessary to protect name. It is also inconvenient to add Synchronized to both methods as mentioned above. In this case, when one thread only accesses ifEquals method, another thread cannot access setName. This is not what we would expect if one thread operated on X and y and the other synchronized operated on name. The reason is that adding synchronized to a method will Monitor the entire Test object as if it were Monitor, meaning that these methods will all be monitored by the same Monitor.

So why are both methods protected when they operate on different resources? Because monitor does not check methods, in fact the reason we synchronize the method is not so that it cannot be called by another thread, but to protect resources. In this case, the Synchronize method does not meet our expectations for multi-threaded operation and requires manual operation. That’s where the Synchronized code block comes in.

    final Object numMonitor=new Object();
    final Object nameMonitor=new Object();
    / / code block
    private void ifEquals(int val){
        synchronize(this){
            x=val;
            y=val
            if(x! =y){
                System.out.println("x: "+x+",y: "+y); }}}/ / code block
    private void delVal(int val){
         synchronize(this){
            x=val-1;
            y=val-1; }}/ / specify the monitor
     private Synchronized void setName(String val){ synchronize(nameMonitor){ name=val; }}public void testThread(a){
       / / ` ` ` ` ` `
   }
Copy the code

Synchronize (Object Object) allows you to specify an Object as a monitor. For example, you can replace this with numMonitor. Another function of synchronize is for data synchronization between threads for monitoring resources

4. Static method synchronize

The default monitor is the current test. Class, not object 2. You cannot use synchronize(this) internally because this is a static method and does not have this. You can use synchronize(test.class)

5. volatile

private volatile int a;

  1. It is lighter than synchronized, making the actions of pairs visible in memory (changes made by one thread are written to memory, making them visible to other threads), and synchronization. Multiple threads reading and writing do not cause data in memory to be tampered with
  2. But double, long, because it’s long, it doesn’t have atomicity like int
  3. Valid for primitive types, valid for objects only if the assignment itself is guaranteed to be valid (Dog Dog =new Dog(” wangwang “), valid for dog.name=”miaomiao”)
  4. We know that (int)a++ is really a two-step operation, so volatile does not guarantee a++
    1. int temp=a+1;
    2. a=temp;

6. Make sure a++ uses AtomicXXX

AtomicInteger a=new ActomicInteger(0);
a.getAndIncrement();
Copy the code

7. Synchronize and operation speed

For example, the ifEquals method, x and Y read the data in memory. The CPU does not directly manipulate the data in memory. Instead, the CPU gives the thread space to operate on the data. As we all know, the current memory speed, compared with the OPERATION speed of the CPU has a huge speed difference, just like the huge speed difference between the hard disk and memory, if the code is executed from the hard disk, and then the operation data is written back to the hard disk, it will be unbearable. The same is true for cpus, where ram is too slow to read and write, so use the CPU’s cache to compensate for the speed difference between memory and the CPU bus, just like using memory to compensate for the speed difference. Based on the above description

When I start operating,

  1. X = 0, y = 0;
  2. Thread1: x=5, y=5 (in the thread’s CPU cache)
  3. Thread2 performs data reading, x=0, y=0;
The synchronize keyword ensures that the CPU reads the assignment before writing it back to memory. To make sure the data is correctCopy the code

As a result, the x=5, y=5 operation would be slow without the CPU cache operation. For example, when testThread() is added to the testThread() method, the execution time varies significantly. So the current cache is necessary despite the data synchronization issues between threads.

8. On safety

A lot of times we talk about security, thread security, network security, data security, but this is a different kind of security:

Safety The Security of Thread Safety is not infringedCopy the code

About the lock

A deadlock

Deadlocks are the type of lock we most often encounter, or hear about. In fact, the cause is very simple, that is, holding each other (each other’s “keys”) leads to waiting for each other:

    private Synchronized void setName(String val){
        synchronize(nameMonitor){
            name=val;
                synchronize(numMonitor){
                    x=val;
                    y=val
                    if(x! =y){
                        System.out.println("x: "+x+",y: "+y);
                    }
                } 
            }
        }
    }
    
    private void ifEquals(int val){
        synchronize(numMonitor){
            x=val;
            y=val
            if(x! =y){
            System.out.println("x: "+x+",y: "+y);
            
            synchronize(nameMonitor){
                    name="haha"; }}}}Copy the code

Thread1 executes ifEquals, numMonitor, and Thread2 executes setName, holding nameMonitor. Thread2 waits until numMonitor is held. Switch back to Thread1, Thread1 finds that it continues to execute, but the nameMonitor is held, and enters the wait, so that the two threads hold each other’s monitor keys and enter the wait for each other, which is a deadlock.

Optimism and pessimism lock

Locks that are not so thread-safe are more database-specific than thread-specific.

For example, when the database is modifying the data, it needs to take out the data first for operation, and then write in, there will be operation A to write the data, and operation B to write the same data. For example, Xiao Ming transfers 100 to me, and OPERATION A gives me my balance X+100, which is about to write into the database: For the balance X+100, Xiao Wang transferred 1 to me and wrote X+1 first. At this point, it would be obviously wrong if A continued writing the balance X+100. There are two ways to solve this problem:

  1. Pessimistic lock: The read/write operation is locked. Operation B waits until operation A finishes. Does it look the same as Synchronize?
  2. Optimistic lock: the data is not locked when the data is retrieved. When operation A writes data, it finds that the database data has changed with the data when it is retrieved, so it recalculates and writes again.

Read-write lock lock

For example, in Test, add a method

    private Lock lock=new ReentrantLock();
    
    private void reset(a){
        lock.lock();
        / / ` ` `
        lock.unlock();
    }

Copy the code

However, if an exception occurs in the middle method and the following lock.unlock() fails to execute, the result is that the lock is always locked (Monitor is automatically unlocked when an exception occurs), so you need to handle this manually:

    private void reset(){ lock.lock(); try{ //``` }finally{ lock.unlock(); }}Copy the code

Seems like synchronized? But why bother with you? But think about it, said before thread synchronization, in writing the data problems, simply read data does not appear problem, only at the time of writing, others, speaking, reading and writing will cause problems, if the thread one reads data switching threads, thread 2 also can’t read, there will be a waste, and the performance of read-write lock can solve this problem:

    private ReentrantReadWriteLock lock=new ReentrantReadWriteLock();
    private ReentrantReadWriteLock.ReadLock readLock=lock.readLock();
    private ReentrantReadWriteLock.WriteLock writeLock=lock.writeLock();
    
    private void ifEquals(int val){
        writeLock.lock();
        try{
            x=val;
            y=val;
        }finally{
            writeLock.unlock()
        }
    }
    
    private readData(a){
        readLock.lock();
        try{
            System.out.println("x: "+x+",y: "+y);
        }finally{ readLock.unlock(); }}Copy the code

This way, when a thread calls ifEquals(), no one else can read or write, and when I call readData(), no one else can read with me. It’s good for performance.

  • Interaction between threads

1. Start with and basic start/finish:
    Thread thread = new Thread() {
        @Override
        public void run() {
            for(int i = 0; i < 1_000_000_000; I++) {// simulate a time-consuming operation log.d ("= = = = = = = = >"."Looking for tangyuan"); }}}; thread.start(); Thread.sleep(100); thread.stop();Copy the code

This completes the beginning and termination of child threads on the main thread, and that’s the basic interaction. But if you use it, stop(). Meow meow meow? Doesn’t it work? Why is the underlined line not recommended? Because you can’t control it, Thread.stop ends regardless of what you’re doing internally (and the main Thread really doesn’t know what the child threads are doing), which leads to uncontrollability. But what if I know that after I do operation A, the next thread is no longer doing any work, and I need to save resources and terminate the thread? Thread.interrupt () is used, but the interrupt is just a mark. If you use the interrupt alone, you need the child thread to act on the interrupt state itself:

    Thread thread = new Thread(new Runnable() {
        @Override
        public void run(a) {
            for (int i = 0; i < 1 _000_000_000; i++) {
                if (isInterrupted()) {// Leave the interrupt state unchanged
                // If (thread.interrupted ()) // This method resets the status of interrupt after being used
                // Do the finishing touches
                    return;
                }
                Log.d("= = = = = = = = >"."Looking for tangyuan"+i); }}}); thread.start();try {
        Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    thread.interrupt();

Copy the code

Does an interrupt sound familiar in the context of thread operations? InterruptedException is used to catch InterruptedException while sleeping. Why do I just want the thread to sleep, to put in a try/catch? There are two reasons:

  1. The sleep method checks the status of the current interrupt and throws InterruptedException if the current thread is interrupted
  2. When we use an interrupt, we need to be aware that it directly wakes up sleep, resetting the state of the interrupt
// A partial comment for the thread.interrupt () method * If this Thread is blockedin an invocation of the wait() or join() or sleep(),
     * methods of this class, then its interrupt status will be cleared and it
     * will receive an {@link InterruptedException}.
     
Copy the code

So when you interrupt a thread you need to consider handling it:

    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            for (int i = 0; i < 1_000_000_000; i++) {
                if (Thread.interrupted()) {//false, 100 milliseconds before it becomes true // for its own interrupt processingreturn; } try { Thread.sleep(2000); // If the catch is not used, the value of the interrupt will be reset. An interrupt that causes an external call equals no} catch (InterruptedException e) {e.printStackTrace(); // Wrap things upreturn;
                }
                Log.d("= = = = = = = = >"."Looking for tangyuan"+i); }}}); thread.start(); try { Thread.sleep(100); } catch (InterruptedException e) {e.printStackTrace();  } thread.interrupt();Copy the code

So in Android, I just want to let the thread sleep, so I don’t need an external interrupt, so I don’t want a try/catch, ok?

SystemClock.sleep(100); // Little brother, understand thisCopy the code
2. Wait (),join(), yield()
  1. Wait (), for example, exists in deadlock situations where both parties need to operate resources and monitor is not in their hands. Therefore, wait and synchronized are always used together because the purpose of a wait is to use a shared resource
String name=null;

private synchronized void setName(a){
    name="Dumplings"; }private synchronized String getName(a){
    return name;
}

private void main(a){
    new Thread() {
        @Override
        public void run(a) {
        // Some operations
        setName();
        }
    }.start();    
    new Thread() {
        @Override
        public void run(a) {
        // Some operations
        getname();
        }
    }.start();   
    
}

Copy the code

Since the two threads do not know which one will execute first, it may appear that getName is executed first, but if getName is empty, it cannot operate. What can be done in this case?

private synchronized String getName() {while(name==null){}// Wait until name is not nullreturn name;
}
Copy the code

But this is a synchronized method that holds the same monitor as setName, and setname is also locked and deadlocked. How do you do that?

private synchronized String getName() {while(name = = null) {/ / usewaitThe standard kit iswhileJudge, notifBecause thewaitWill be woken up by the interrupt try{wait(a); // Object method}catch(InterruptedException e){// e.printStackTrace(); }}// Wait until there is no empty spacereturn name;
}
private synchronized void setName(){
    name="Dumplings"; notifyAll(); // call all threads on monitor that are waiting to see if the execution condition is met}Copy the code

Or to enter the page, need to request multiple interfaces, according to the interface data to set the page Settings, the same situation. In fact, a SINGLE Rxjava ZIP operation solves most of these problems. Actually write so much, this need Rxjava zip operation solves…

  1. Join () may have a thread1 in progress, requiring thread0 to execute, and then resuming the thread when it has finished
private void main(){
    new Thread() {
        @Override
        public void run() {// Some operations try{thread0.join(); // Equivalent to notify automaticallywait//Thread.yield(); }catch(InterruptedException e){ e.printStackTrace(); } // Some operations getName (); }}; }Copy the code
  1. Yield () is rarely used, so see the line commented out above to yield the CPU elapsed time slice to a thread of the same priority.

Thanks & Reference: throw line