Concurrency Basics (1) Introduction to Threads

An introduction to threads

A thread, sometimes referred to as a Lightweight Process (LWP), is the smallest unit of program execution flow. A standard thread consists of the thread ID, the current instruction pointer (there is a program counter, which is used to store the address of the unit where the next instruction is located), and the set of registers (registers are part of the central processing unit. Registers are limited memory The high-speed storage unit of storage capacity, they can be used to temporarily store instruction, data and address.In the control unit of central processing unit, the register that contains has instruction register (IR) and program counter (PC).In the arithmetic of central processing unit And in the logic part, the register has the accumulator (ACC).) and the stack.

In addition, a thread is an entity in the process, and it is the basic unit independently scheduled and dispatched by the system. The thread itself does not own system resources, but only a few resources that are necessary for running. Threads share all the resources owned by the process. A thread can create and cancel another thread, and multiple threads in the same process can execute concurrently. Due to the mutual constraints (caused by shared resources) between threads, the threads are intermittent in their operation. Threads also have three basic states: ready, blocked, and running. The ready state means that the thread has all the conditions for running, it can run logically, and is waiting for the processor; the running state means that the thread occupies the processor and is running; the blocking state means that the thread is waiting for an event (such as a semaphore), the logic is not executable. Every program has at least one thread, and if the program has only one thread, it is the program itself.

A thread is a single sequential flow of control in a program. A relatively independent and schedulable execution unit in a process is the basic unit for the system to independently schedule and assign CPUs, which refers to the scheduling unit of the running program. Running multiple threads simultaneously in a single program to complete different work is called multithreading.

Features

In a multi-threaded OS, a process usually includes multiple threads, and each thread is the basic unit for utilizing the CPU and is an entity that spends the least overhead. Threads have the following properties. 1) Lightweight entities

The entities in the thread basically do not own system resources, but only have some necessary resources that can guarantee independent operation.

The entity of thread includes program, data and TCB. Thread is a dynamic concept, and its dynamic characteristics are described by the Thread Control Block (TCB). TCB includes the following information: (1) Thread status. (2) On-site resources that are saved when the thread is not running. (3) A set of execution stacks. (4) The local variable main memory area for each thread is stored. (5) Access to main memory and other resources in the same process.

A set of registers and stacks used to indicate the program counter of the sequence of instructions to be executed, reserved local variables, few state parameters and return addresses, etc. 2) The basic unit of independent scheduling and dispatch.

In a multithreaded OS, a thread is the basic unit that can run independently, and thus is also the basic unit of independent scheduling and dispatch. Since threads are "light", thread switching is very fast and inexpensive (within the same process). 3) can be executed concurrently.

Multiple threads in a process can execute concurrently, even allowing all threads in a process to execute concurrently; similarly, threads in different processes can also execute concurrently, making full use of the processor and peripheral devices The ability to work in parallel. 4) Shared process resources.

Each thread in the same process can share the resources owned by the process, which is first manifested in: all threads have the same address space (the address space of the process), which means that the thread can access every part of the address space. A virtual address; in addition, open files, timers, semaphore mechanisms, etc. owned by the process can be accessed. Since threads within the same process share memory and files, there is no need to call the kernel to communicate with each other.

Compare with process (easy to confuse)

A process is the basic unit of resource allocation. All resources related to the process are recorded in the process control block PCB. to indicate that the process owns these resources or is using them.

In addition, the process is also the scheduling unit that preempts the processor, (the process is like a performance team, and the processor can be described as a stage, and the thread can be described as the individual of the performance team) It has a complete virtual address space (the virtual address space available to the process The address range is called the virtual address space of that process (When the processor reads or writes a memory location, it uses the virtual address. As part of the read or write operation, the processor translates the virtual address to a physical address.)). When a process is scheduled, different processes have different virtual address spaces, and different threads within the same process share the same address space.

Corresponding to a process, a thread has nothing to do with resource allocation. It belongs to a certain process and shares the resources of the process with other threads in the process.

A thread only consists of the relevant stack (system stack or user stack) registers and thread control table TCB. Registers can be used to store local variables within a thread, but not variables related to other threads.

Usually in a process can contain several threads, they can use the resources owned by the process. In operating systems that introduce threads, processes are usually used as the basic unit for allocating resources, and threads are used as the basic unit for independent operation and independent scheduling. Since a thread is smaller than a process and basically does not own system resources, the overhead for its scheduling will be much smaller, which can more efficiently improve the degree of concurrent execution among multiple programs in the system, thereby significantly improving system resources. utilization and throughput. Therefore, the general-purpose operating systems introduced in recent years have introduced threads to further improve the concurrency of the system, and regard it as an important indicator of modern operating systems.

The difference between threads and processes can be summarized in the following four points: 1) Address space and other resources (such as open files): processes are independent of each other, and are shared among threads of the same process. Threads within a process are not visible to other processes. 2) Communication: Inter-process communication IPC, threads can directly read and write process data segments (such as global variables) for communication - the assistance of process synchronization and mutual exclusion means is needed to ensure data consistency. 3) Scheduling and switching: Thread context switching is much faster than process context switching. 4) In a multithreaded OS, a process is not an executable entity.

The basic knowledge points of threading can be summarized into a few points:

  • A thread is a thread of execution in a program. The Java Virtual Machine allows an application to run multiple threads of execution concurrently.
  • Each thread has a priority, and the execution of high-priority threads takes precedence over low-priority threads. But you should not arrange the execution order of threads by setting thread priorities, which will be detailed later.
  • Each thread may or may not be marked as a daemon thread. That is, in java, threads are divided into two categories: user threads and daemon threads.
  • When the java virtual machine starts, there will be a non-daemon thread (ie user thread) to start running. This thread usually calls the main method of the specified class. Simply put, when you execute the main method of a class, it is actually running as a thread, that is, the main thread.
  • Threads do not own resources in the operating system, and threads share the resources of processes. That is, multiple threads belonging to the same process may have mutually exclusive access to resources. This involves the concept of locks. At the same time, in the JVM, in addition to using locks to solve the problem of concurrency, each thread can also have a private resource - thread copy (ThreadLocal), so that threads do not need to compete for resources.

2. User thread and daemon thread

There are two types of threads in Java: User Thread(user threads), Daemon Thread(daemon threads)

To use a more common example, any daemon thread is the nanny of all non-daemon threads in the entire JVM:

As long as there is any non-daemon thread in the current JVM instance that has not ended, the daemon thread will all work; only when the last non-daemon thread ends, the daemon thread will end work together with the JVM. The role of Daemon is to provide convenient services for the operation of other threads. The most typical application of daemon threads is GC (garbage collector), which is a very competent guardian.

There is almost no difference between User and Daemon, the only difference is the departure of the virtual machine : if the User Thread has all exited, only the Daemon Thread exists, and the virtual machine will exit. Because there is no guardian, the Daemon has no work to do, and there is no need to continue running the program.

It is worth mentioning that daemon threads are not only provided inside the virtual machine, and users can also set daemon threads themselves when writing programs. The following method is used to set the daemon thread.

    Thread daemonTread = new Thread();  
       
      // 设定 daemonThread 为 守护线程,default false(非守护线程)  
     daemonThread.setDaemon(true);  
       
     // 验证当前线程是否为守护线程,返回 true 则为守护线程  
     daemonThread.isDaemon();  

There are a few things to note here:

(1) thread.setDaemon(true)It must be thread.start()set before, otherwise an IllegalThreadStateException will run out. You cannot set a running regular thread as a daemon thread. (2) DaemonThe new thread generated in the thread is also Daemon's. (3) Do not think that all applications can be assigned Daemonto perform services, such as read and write operations or computational logic.

Because you can't know whether the Daemon has completed the expected service task before all the Users have completed. Once the User exits, a large amount of data may not have time to be read in or written out, and the calculation task may run multiple times with different results. This is devastating to the program. The reason for this result has already been said: once all User Threads leave, the virtual machine will exit the running//complete the daemon thread task of file output

    import java.io.*;     
        
    class TestRunnable implements Runnable{     
        public void run(){     
                   try{     
                      Thread.sleep(1000);//守护线程阻塞1秒后运行     
                      File f=new File("daemon.txt");     
                      FileOutputStream os=new FileOutputStream(f,true);     
                      os.write("daemon".getBytes());     
               }     
                   catch(IOException e1){     
              e1.printStackTrace();     
                   }     
                   catch(InterruptedException e2){     
                      e2.printStackTrace();     
               }     
        }     
    }     
    public class TestDemo2{     
        public static void main(String[] args) throws InterruptedException     
        {     
            Runnable tr=new TestRunnable();     
            Thread thread=new Thread(tr);     
                    thread.setDaemon(true); //设置守护线程     
            thread.start(); //开始执行分进程     
        }     
    }     
    //运行结果:文件daemon.txt中没有"daemon"字符串。

See, how terrible it is to wrap the input and output logic into a daemon thread, the string is not written to the specified file. The reason is also very simple, until the main thread is finished, the daemon thread is still blocked for 1 second. At this time, the main thread runs quickly, the virtual machine exits, the Daemon stops the service, and the output operation naturally fails.

    public class Test {  
      public static void main(String args) {  
      Thread t1 = new MyCommon();  
      Thread t2 = new Thread(new MyDaemon());  
      t2.setDaemon(true); //设置为守护线程  
      t2.start();  
      t1.start();  
      }  
      }  
      class MyCommon extends Thread {  
      public void run() {  
      for (int i = 0; i < 5; i++) {  
      System.out.println("线程1第" + i + "次执行!");  
      try {  
      Thread.sleep(7);  
      } catch (InterruptedException e) {  
      e.printStackTrace();  
      }  
      }  
      }  
      }  


  

    class MyDaemon implements Runnable {  
      public void run() {  
      for (long i = 0; i < 9999999L; i++) {  
      System.out.println("后台线程第" + i + "次执行!");  
      try {  
      Thread.sleep(7);  
      } catch (InterruptedException e) {  
      e.printStackTrace();  
      }  
      }  
      }  
      }  

Output result:

The 0th execution of the background thread! Thread 1 executes for the 0th time! Thread 1 executes for the first time! The first execution of the background thread! The background thread is executed for the second time! Thread 1 executes for the second time! Thread 1 executes for the 3rd time! The background thread is executed for the 3rd time! Thread 1 executes for the 4th time! The 4th execution of the background thread! The 5th execution of the background thread! The 6th execution of the background thread! The 7th execution of the background thread! Process finished with exit code 0 It can be seen from the above execution results: The foreground thread is guaranteed to be executed, and the background thread exits before the execution is completed.

In fact: JRE's criterion for judging whether the program execution ends is that all foreground execution threads are completed, regardless of the status of background threads. Therefore, you must pay attention to this issue when using background counties.

Additional instructions:

**Definition:** Daemon thread -- also known as "service thread", will automatically leave when there are no user threads to serve.

**Priority:** The daemon thread has a lower priority and is used to provide services to other objects and threads in the system.

**Setting:** By setDaemon(true)setting the thread as a "daemon thread"; the way to set a user thread as a daemon thread is to use the setDaemon method of the thread object before the thread object is created.

Example: The garbage collection thread is a classic daemon thread. When there is no longer any running Thread in our program, the program will no longer generate garbage, and the garbage collector will have nothing to do, so when the garbage collection thread is the JVM When the only remaining threads are left, the garbage collection thread automatically leaves. It always runs in a low-level state for real-time monitoring and management of recyclable resources in the system.

**Life cycle:** Daemon is a special process that runs in the background. It is independent of the controlling terminal and periodically performs some kind of task or waits for some event to occur. That is to say, the daemon thread does not depend on the terminal, but depends on the system and "lives and dies" with the system. What does Java's daemon thread look like? When all threads in the JVM are daemon threads, the JVM can exit; if there are one or more non-daemon threads, the JVM will not exit.

Practical application example:

In the comet server push technology that uses long connections, the message push thread is set as a daemon thread, serving the servlet user thread of ChatServlet, and the message thread is started in the servlet init. Once the servlet is initialized, the server will always exist. After the servlet is destroyed, the message Thread exits automatically

When the container receives a servlet request, the scheduling thread selects a worker thread from the thread pool, passes the request to the worker thread, and then executes the service method of the servlet by the thread. When this thread is executing, the container receives another request, and the scheduling thread also selects another worker thread from the thread pool to serve the new request. The container does not care whether the request accesses the same servlet. When When the container receives multiple requests to the same servlet at the same time, the service() method of the servlet will be executed concurrently in multiple threads. By default, the servlet container uses a single-instance multi-threading method to process requests, which reduces the overhead of generating servlet instances and improves the response time to requests. For Tomcat, the number of threads in the thread pool can be set through the <Connector> element in server.xml. . As shown in the figure:

write picture description here

Why use daemon threads?

We know that static variables are at the ClassLoader level, and if the web application stops, these static variables are also cleared from the JVM. But threads are at the JVM level. If you start a thread in a web application, the life cycle of the thread will not be synchronized with the web application. That is, even if you stop the web application, the thread is still active. It is precisely because of this very obscure problem that many experienced developers are not in favor of privately starting threads in Web applications.

If we manually use the JDK Timer (Quartz's Scheduler), start the Timer when the web container starts, and when the web container shuts down, unless you manually close the Timer, the tasks in the Timer will continue to run!

The following is a small example to demonstrate this "weird" phenomenon. We ServletContextListenercreate a Timer when the web container starts and run a task periodically:

    //代码清单StartCycleRunTask:容器监听器  
    package com.baobaotao.web;  
    import java.util.Date;  
    import java.util.Timer;  
    import java.util.TimerTask;  
    import javax.servlet.ServletContextEvent;  
    import javax.servlet.ServletContextListener;  
    public class StartCycleRunTask implements ServletContextListener ...{  
        private Timer timer;  
        public void contextDestroyed(ServletContextEvent arg0) ...{  
            // ②该方法在Web容器关闭时执行  
            System.out.println("Web应用程序启动关闭...");  
        }  
        public void contextInitialized(ServletContextEvent arg0) ...{  
             //②在Web容器启动时自动执行该方法  
            System.out.println("Web应用程序启动...");  
            timer = new Timer();//②-1:创建一个Timer,Timer内部自动创建一个背景线程  
            TimerTask task = new SimpleTimerTask();  
            timer.schedule(task, 1000L, 5000L); //②-2:注册一个5秒钟运行一次的任务  
        }  
    }  
    class SimpleTimerTask extends TimerTask ...{//③任务  
        private int count;  
        public void run() ...{  
            System.out.println((++count)+"execute task..."+(new Date()));  
        }  
    }  

web.xmlDeclare this web container listener in :

<?xml version="1.0" encoding="UTF-8"?>
<web-app>
<listener>
<listener-class>com.baobaotao.web.StartCycleRunTask</listener-class>
</listener>
</web-app>

After deploying this web application in Tomcat and starting it, you will see the task execute every 5 seconds. After running for a period of time, log in to the Tomcat management background and close the corresponding Web application (chapter13).

Go to the Tomcat console and you will see that although the web application is closed, the Timer task is still executing as usual - the stage has been removed and the actors continue to perform:

We can end the task by changing the code in the listing StartCycleRunTask, contextDestroyed(ServletContextEvent arg0)adding timer.cancel()code in it, and manually stopping the Timer after the web container shuts down.

Spring provides JDK Timerand can be associated with the life cycle of the Spring container, starting the scheduler when the Spring container starts, and stopping the scheduler when the Spring container shuts down. Therefore, configuring the scheduler through these two FactoryBeans in Spring, and then obtaining the scheduler reference from Spring IoC for task scheduling will not cause the problem that the Web container is closed and the task is still running. And if you use Timer or Scheduler directly in the program, this problem will occur without additional processing.Quartz SchedulerTimerFactoryBeanSchedulerFactoryBean

##### References

If there is something inappropriate in the article, please correct me. You can also pay attention to my WeChat public account: 好好学java, get high-quality learning resources, or join the QQ technical exchange group: 766946816, let's talk about java.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325776413&siteId=291194637