Secret Context Switching

Keywords: Java Redis Programming

What is context switching?

In fact, at the time of a single processor, the operating system can handle multithreaded concurrent tasks.The processor allocates a CPU Time Slice to each thread, and the thread executes the task within the Time Slice allocated.

A CPU time slice is a period of time, typically tens of milliseconds, that the CPU allocates to each thread for execution.Threads switch from one another in such a short period of time that we don't even feel it, so it looks like it's happening at the same time.

The time slice determines how long a thread can continuously occupy the processor.When a thread runs out of time or is forced to pause for its own reasons, another thread (which can be the same thread or another process) is selected by the operating system to occupy the processor.This process in which one thread is suspended and deprived of usage and the other thread is selected to start or continue is called Context Switch.

Specifically, a thread that is suspended from running because it is deprived of processor usage is "cut out"; a thread that is selected to occupy the processor to start or continue running is "cut in".In this cut-out process, the operating system needs to save and restore the corresponding progress information, which is the "context".

Context switching is when one working thread is paused by another thread, which occupies the processor's starting process.Spontaneous and non-spontaneous invocation of actions by the system and Java programs results in context switching, which incurs overhead.

Reasons for multithreaded context switching

Before you start, look at the life cycle state of the system threads.

Combined with the diagram, the threads are mainly in five states: New (NEW), Ready (RUNNABLE), Running (RUNNING), Blocked (BLOCKED), and Dead (DEAD).

In multithreaded programming, executing calls to the following methods or keywords often results in spontaneous context switching.

sleep(),wait(),yield(),join(),park(),synchronized,lock

Performance issues caused by context switching

We've always said that context switching incurs overhead. Next, I'll use a piece of code to compare the speed of concurrent and concurrent executions:

   public class DemoApplication {
       public static void main(String[] args) {
              //Run multithreaded
              MultiThreadTester test1 = new MultiThreadTester();
              test1.Start();
              //Run Single Thread
              SerialTester test2 = new SerialTester();
              test2.Start();
       }

   static class MultiThreadTester extends ThreadContextSwitchTester {
          @Override
          public void Start() {
                 long start = System.currentTimeMillis();
                 MyRunnable myRunnable1 = new MyRunnable();
                 Thread[] threads = new Thread[4];
                 //Create multiple threads
                 for (int i = 0; i < 4; i++) {
                       threads[i] = new Thread(myRunnable1);
                       threads[i].start();
                 }
                 for (int i = 0; i < 4; i++) {
                       try {
                              //Wait to finish running together
                              threads[i].join();
                       } catch (InterruptedException e) {
                              e.printStackTrace();
                       }
                 }
                 long end = System.currentTimeMillis();
                 System.out.println("Multithreaded Runtime: " + (end - start) + "ms");
                 System.out.println("count: " + counter);
          }
          // Create a class that implements Runnable
          class MyRunnable implements Runnable {
                 public void run() {
                       while (counter < 100000000) {
                              synchronized (this) {
                                     if(counter < 100000000) {
                                            increaseCounter();
                                     }

                              }
                       }
                 }
          }
   }

  //Create a single thread
   static class SerialTester extends ThreadContextSwitchTester{
          @Override
          public void Start() {
                 long start = System.currentTimeMillis();
                 for (long i = 0; i < count; i++) {
                       increaseCounter();
                 }
                 long end = System.currentTimeMillis();
                 System.out.println("Single Thread Runtime: " + (end - start) + "ms");
                 System.out.println("count: " + counter);
          }
   }

   //Parent Class
   static abstract class ThreadContextSwitchTester {
          public static final int count = 100000000;
          public volatile int counter = 0;
          public int getCount() {
                 return this.counter;
          }
          public void increaseCounter() {

                 this.counter += 1;
          }
          public abstract void Start();
   }
}

After execution, look at the time test results for both:

From the data comparison, we can see that the execution speed of concurrent execution is faster than that of concurrent execution.This is because thread context switching incurs additional overhead, generally using the Synchronized lock keyword, which results in competition for resources, which causes context switching, but even if the Synchronized lock keyword is not used, concurrent execution cannot exceed the concurrent execution speed because multithreads also have context switching.The design of Redis and NodeJS well reflects the advantages of single-threaded serial.

summary

The more threads, the faster the system will not necessarily run.So when do we use single threading and when do we use multi-threading when we usually have a large amount of concurrency?

In general, we can use a single thread when a single logic is relatively simple and relatively fast.For example, Redis, as we mentioned earlier, reads values from memory quickly, regardless of blocking caused by I/O bottlenecks.In scenarios with relatively complex logic, relatively long wait times, or scenarios that require a lot of computation, I recommend using multithreading to improve the overall performance of the system.For example, NIO-era file reading and writing operations, image processing, and large data analysis.

Posted by slough on Thu, 04 Jun 2020 10:52:49 -0700