Multithreading Learning Part 1

previewfile_2862005448123

Motto: If the sky is healthy, a gentleman will strive to strengthen himself; if the terrain is weak, a gentleman will carry things with great virtue.

The purpose of introducing the concept of process

The main purpose of introducing the concept of process is to solve the problem of "concurrent programming".
This is because the CPU has entered the era of multi-core. If
you want to further improve the execution speed of the program, you need to make full use of the multi-core resources of the CPU.

In fact, multi-process programming can already solve the problem of concurrent programming. It can already use CPU multi-core resources.
But it brings new problems => That is, the process is too heavy (consumes more resources & is slow)!! !
Creating a process is relatively expensive.
Destroying a process is relatively expensive.
Scheduling a process is relatively expensive.

Although the process can solve the problem, it is not the optimal solution to this problem. It is
expected that a concept with less overhead and lighter weight can solve this "concurrent programming".
Therefore, threads came into being!!! Threads are also called threads. "Lightweight process"
makes the creation, destruction, and scheduling faster on the premise of solving the problem of "concurrent programming"!!!

Process is the basic unit in the operating system . When you create a process, you have to allocate some resources to it (such as memory resources and hard disk resources...).
When allocating resources, you have to spend some time. The process is important, and the main focus is on "resource allocation." /recycle" on.

Why are threads more lightweight???

=> It saves the operations of applying for resources/releasing resources.

Take the expansion of the factory as an example:
Plan 1 : Build a branch factory (Factory No. 2) exactly like the original factory in another place
~~ It requires a new site, a new logistics system, and a new production line...
=> Multi-process version solution

image-20230918093634503

Option 2 : Expand the original factory and add a production line~~ The cost is much smaller than the first option
~~ The site and logistics system can be reused, and only a new production line is needed...
~~ Multi-threaded version solution => At this time, it is only necessary to apply for resources for the first set of production lines. If
new production lines are added later, the previous resources can be reused.

image-20230918094220221

The relationship between threads and processes

~~ It means that the process contains threads.
A process can contain one thread or multiple threads. (It cannot be without)
When only the first thread is started, the overhead is relatively large. Subsequent threads will save trouble.

Multiple threads in the same process share the same resources of the process (mainly memory and file descriptor tables )

  • Memory ~~ The object of thread 1new can be used directly in threads 2, 3, and 4.
  • File description table ~~Files opened by thread 1 can be used directly in threads 2, 3, and 4

When the operating system is actually scheduled, it is scheduled in units of threads.
(The previous blog talked about process scheduling, which is equivalent to a situation where there is only one thread in each process)

If each process has multiple threads, each thread is scheduled independently on the CPU
=> Threads are the basic unit of operating system scheduling and execution.

Can a thread be in multiple processes?
No! There is a one-to-many relationship between processes and threads.

One thread is executed on one core. If you have two threads in a process,
thread 1 may be executed on core A, and thread 2 may be executed on core B.
When the operating system schedules, it does not actually care about the process, but only care thread

A thread is also described by a PCB.
A process may correspond to one PCB, or it may correspond to multiple PCBs. As
introduced before, the status, context, priority, and accounting information in the PCB are all unique to each thread. Each records its own.
But between PCBs in the same process, the pid is the same, and the memory pointer and file representation table are also the same
=> They are distinguished by the same pid as being the same process.

Speaking of "scheduling", it has nothing to do with processes.
Processes are responsible for resource allocation.
Threads take over everything related to scheduling .

Theory is to guide us in programming. If you don’t understand this theory,
I don’t know why this code is written like this!!!

Graphical understanding

The initial requirement is
1 funny old man, eating 100 chickens~~ The efficiency is relatively low
image-20230918154821678

How to eat faster, there are two versions: multi- process and multi-thread , as shown below:
The multi-process way of eating chicken:
~~ The cost is relatively high

image-20230918155101436

Multi-threaded chicken game:

image-20230918155347489Can the speed be faster by increasing the number of funny old guys here???

image-20230918160353714

But if we continue to increase the number of funny old guys???
image-20230918163513486

(1) In the case of multi-threading, multiple funny old guys share the same chicken, and they may fight at this time.

image-20230918170001294

(2) There is another situation with multi-threading~~

image-20230918163927661

Note: Chrome browser needs to use a multi-process programming model (each tab page is a process)
~~ The purpose is to prevent one page from hanging and taking other pages away.

When is there a security issue???

When multiple execution flows access the same shared resource.
~~ The thread model is naturally resource sharing. It is very easy to trigger multiple threads competing for the same resource (the same variable).
~~ The process model is naturally resource isolated , not easy to trigger.
When communicating between processes, multiple processes accessing the same resource may cause problems.

Multi-threading will improve efficiency, but it is not as safe as multi-process.
But if the code is well written, thread safety issues will not be worried
=> The current multi-thread programming model is more widely used than the multi-process model.
When you encounter multi-threading at work There are many more situations than multi-process. Especially in the Java circle.

In interviews, the difference between processes and threads is the most frequently asked question in the "operating system" module, bar none!!!




How to do multi-threaded programming in Java

The operations on threads are all APIs provided by the operating system.
Java is a cross-platform language. Many functions provided by the operating system are encapsulated by the JVM.
We do not need to learn the system’s native APIs, we only need to learn the ones provided by Java. Just API.


How does Java achieve cross-platform???
  • windows system, implements a windows version of JVM

  • Linux system, implements the Linux version of JVM

  • Mac system, implements the Mac version of JVM

  • Any system also implements the corresponding version of JVM

Cross-platform is supported by countless different versions of JVM!
These different JVMs encapsulate the APIs of different systems, and all of them are bytecodes that execute the same rules.
Example: You travel around Europe, check in to various countries, and
arrive in the UK. I hired a British translator (who speaks both English and Chinese) to help me communicate with the locals;
when I arrived in France, I hired a French translator (who spoke both French and Chinese) to help me. Communicate with the locals;
when I arrived in Germany, I found a German translator (who can speak both German and Chinese) to help me communicate with the locals; ...
Java
is cross-platform, so there are several platforms. Several "translations"...
~~ Comments: "Strong efforts can produce miracles!!!"


Java operates multi-threads, the core class Thread

~~ When using the Thread class, there is no need to import other packages, because it is under java.lang.
Similar ones are: String, StringBuilder, StringBuffer

When creating a thread, we hope that the thread will become an independent execution stream, and it must be able to execute a piece of code.

The first multi-threaded program

code show as below:

class MyThread extends Thread{
    
    
    @Override
    public void run() {
    
    
        while(true){
    
    
            System.out.println("hello thread");
            // 为了让这里的打印慢点, 方便看, 加个sleep, 休眠 1s
            try {
    
    
                Thread.sleep(1000);
            }catch (InterruptedException e){
    
    
                e.printStackTrace();
            }
        }
    }
}

public class ThreadDemo1 {
    
    
    public static void main(String[] args) {
    
    
        Thread t = new MyThread();
        t.start();

        while(true){
    
    
            System.out.println("hello main");
            try {
    
    
                Thread.sleep(1000);
            }catch (InterruptedException e){
    
    
                e.printStackTrace();
            }
        }
    }
}

operation result:

image-20230918212013737

When the operating system schedules threads, it performs " preemptive execution ". It is not certain which thread comes first and which thread comes last. It depends on the specific implementation strategy of the operating system scheduler.
Although there is a priority, it cannot be modified at the application level. .From
the perspective of the application (code), the effect is as if the scheduling order between threads is "random". => The
kernel itself is not random. But there are too many intervening factors, and the application program The details cannot be perceived at the first layer,
so it can only be considered random!!!
The execution order is implemented by the kernel, there is no solution, but limited intervention can be carried out through some APIs. The
culprit of thread safety problems is the most evil. The source is the preemptive execution and random scheduling here .

The Thread class here is essentially an encapsulation of threads in the system.
Each Thread object corresponds to a thread in the system (corresponding to a PCB).

Method Override: The parent class has a method, and the subclass has a method with the same name and the same parameters.
The method of the subclass will be called by the dynamic binding mechanism.
Thead t = new MyThread();
t.run();Although t is a reference to the parent class, the run called here is still the method of the subclass. (t is essentially the object of the subclass pointed to)

Overload: the same scope, multiple methods with the same name, different number or type of parameters,
in the same class, or between a subclass and a parent class...

t.sart();=> A special method in threads to start a thread.
Start does not call run, but creates a thread. The new thread executes the run method.
How to create a new thread in start => It is to call the API of the operating system,
through The operating system kernel creates the PCB (program control block) of the new thread and hands the instructions to be executed to this PCB.
When the PCB is scheduled to be executed on the CPU, the code in the thread's run method is also executed.
If the run method After execution is completed, the new thread is naturally destroyed.

This operation of the new Thread object does not create a thread. (The thread mentioned here refers to the PCB in the system kernel) The
PCB is created by calling start, and only then can there be a real thread.

The difference between start and run:
start actually creates a thread (created from the system), and the thread is an independent execution flow.
run just describes what the thread is going to do. If you call run directly in main, No new threads are created at this time, the main thread is all working alone.

PCB corresponds to threads. One thread corresponds to one PCB and one process corresponds to multiple PCBs.
If a process has only one thread, it is one process to one PCB.
Several PCBs in the same process have the same pid. The pids of different processes are different. .
PCB is not an "abbreviation", it is a data structure that reflects how processes/threads are implemented and how they are described.
PCB is just a concept mentioned in operating system books. In fact, the name of the corresponding structure in Linux is task_struct

How to visually see the two threads in the above code??? You can use the tool jconsole
that comes with the JDK to view all threads in the current Java process.
image-20230918215407492

Note: JDK => Java Development Kit, which contains many tools, not just javac and java.
image-20230918220407913

Note: Processes contain threads => If you want to see threads, you must first find the corresponding process, and then see which threads are in the process.

Click thread.ThreadDemo1 in the picture above
image-20230918220859922

image-20230918221747327

If you open JConsole and see nothing in the local process, try right-clicking and running as administrator.

image-20230918224445511



Various ways to create threads in Java
  1. Inherit Thread and override the run method

    1. Inherit Thread to create a thread class
    2. Create an instance of the MyThread class
    3. Call the start method to start the thread

    
    class MyThread extends Thread{
          
          
        @Override
        public void run() {
          
          
            System.out.println("hello thread");
        }
    }
    public class ThreadDemo1 {
          
          
        public static void main(String[] args) {
          
          
            Thread t = new MyThread();
            t.start();
        }
    }
    
  2. Implement the Runnable interface

    1. Implement the Runnable interface
    2. Create a Thread class instance and use the Runnable object as the target parameter when calling the Thread constructor
    3. Call the start method

    The function of Runnable is to describe a "task to be executed", and the run method is the execution details of the task.

    class MyRunnable implements Runnable {
          
          
        @Override
        public void run() {
          
          
            System.out.println("hello thread");
        }
    }
    public class ThreadDemo2 {
          
          
        public static void main(String[] args) {
          
          
            // 这只是描述了一个任务
            Runnable runnable = new MyRunnable();
            // 把任务交给线程来执行
            Thread t = new Thread(runnable);
            t.start();
        }
    }
    

    The advantage of writing this way:
    decoupling. The purpose is to separate threads from the work that threads do.
    If you want to change the code in the future, you don’t need to use multiple threads, use multiple processes, or thread pools, or coroutines... At this time, the code The changes are relatively minor.

  3. Use anonymous inner classes and inherit from Thread

    1. Create a subclass of Thread. (The subclass has no name) so it is called "anonymous"
    2. Create an instance of the subclass and let the t reference point to the instance

    public class ThreadDemo3 {
          
          
        public static void main(String[] args) {
          
          
            Thread t = new Thread(){
          
          
                @Override
                public void run() {
          
          
                    System.out.println("hello");
                }
            };
            t.start();
        }
    }
    
  4. Use anonymous inner classes to implement Runnable

    This writing method is essentially the same as method 2.
    It is just a syntax that assigns the task of implementing Runnable to an anonymous inner class.
    Here, a class is created, Runnable is implemented, an instance of the class is created, and it is passed to the constructor of Thread.

    public class ThreadDemo4 {
          
          
        public static void main(String[] args) {
          
          
            Thread t =new Thread(new Runnable() {
          
          
                @Override
                public void run() {
          
          
                    System.out.println("hello");
                }
            });
            t.start();
        }
    }
    
  5. Use Lambda expression~~ The simplest and recommended way of writing

    Use lambda expressions to describe tasks and
    directly pass lambda to the Thread constructor.
    Lambda is an anonymous function (a function without a name) that is destroyed after being used once.
    Functions in Java cannot exist without classes. In order to be compatible with other languages, Java Alignment, made a crappy functional interface, and implemented lambda through this.

    public class ThreadDemo5 {
          
          
        public static void main(String[] args) {
          
          
            Thread t = new Thread(()-> {
          
          
                System.out.println("hello");
            });
            t.start();
        }
    }
    

Guess you like

Origin blog.csdn.net/m0_73740682/article/details/133002382