[JavaEE] Multithreading in Java (Thread class)

Author homepage:paper jie_blog

Author of this article: Hello everyone, I am paper jie. Thank you for reading this article. Welcome to Yijiansanlian.

This article is included in the "JavaEE" column. This column is carefully created for college students and programming novices. The author spent a lot of money (time and energy) to build it and cover all the basic knowledge of MySQL. I hope it can help readers.

Other columns: "MySQL", "C Language", "javaSE", "Data Structure", etc.

Content sharing: This issue will share an important multi-threading class Thread in JavaEE~

Table of contents

What is Thread

Create thread

Inherit the Thread class

Implement the Runnable interface

Anonymous inner class creates Thread subclass object

Anonymous inner class creates Runnable subclass object

lambda expression creates subclass object

Thread class methods and common attributes

Construction method

Common properties

Common methods

Start a thread - start()

Interrupt thread 

Introducing tags

interrupt()

Reason for exception

Waiting program - join()

Get the current thread reference - currentThread()


What is Thread

The Thread class is in the Java standard library, and it can be regarded as a further abstraction and encapsulation of the API provided by the operating system. We can think of a Thread instance object as a thread.

Create thread

Inherit the Thread class

Here you need to create a thread class following the Thread class. Because the run method needs to be overridden here, which contains the logic that this thread needs to execute.

Then you need to create an instance of it so that a thread can be created. Finally, you need to 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();
        System.out.println("hello main");
         
    }
}

Implement the Runnable interface

Create a Thread instance and pass the Runnable object as a parameter when calling the Thread constructor.

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("线程运行代码");
    }
}
public class ThreadDemo2 {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable());
        t.start();
        System.out.println("hell main");

    }
}

Anonymous inner class creates Thread subclass object

Behind this is an anonymous subclass of the Thread class.

public class ThreadDemo3 {
    public static void main(String[] args) {
        Thread t = new Thread("这是我"){
            @Override
            public void run() {
                System.out.println("这是匿名方法");
                while(true) {
                    System.out.println("heeh");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        t.start();
    }
}

Anonymous inner class creates Runnable subclass object

public class ThreadDemo4 {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable(){
            @Override
            public void run() {
                System.out.println("匿名创建Runnable子类");
            }
        });
        t.start();
    }
}

lambda expression creates subclass object

public class ThreadDemo5 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            System.out.println("lambda表达式创建子类对象");
        });
        t.start();
    }
}

Thread class methods and common attributes

Construction method

The third and fourth here can be renamed for better debugging.

Common properties

ID is the unique representation of this thread, and different thread IDs will not be repeated.

The name is generally used during debugging.

The state represents the current situation of a thread. Generally, there are ready state and blocking state.

Priority indicates whether a thread is more likely to be scheduled

The running of the foreground thread will prevent the end of the process, and the running of the background thread will not prevent the end of the process. The end of a process needs to wait for all foreground threads to finish executing. Otherwise, even if the main thread is executed, it will not end. The threads we create are all foreground threads by default.

Whether it is alive or not is simply whether the run method has been executed. Although the Thread instance in Java represents multi-threading, its life cycle and the thread PCB created in the kernel The life cycles are not the same.

Although the Thread instance object exists at this time, the thread in the kernel has not been created yet, and isAlive is false. The thread in the kernel will only be created after t.start() is executed, and isAlive is true at this time.

When the run method is executed, the thread in the kernel is destroyed, and isAlive is false at this time.

Common methods

Start a thread - start()

Although the Thread instance object is created here, the thread is not actually started. A thread is truly created only after calling start(). The start( here ) is to create a thread in the kernel. Calling start() to create a thread is essentially calling the system API to create a thread.

Here, a thread can only call start() once. To use start again, you need to call it with another thread object. Otherwise, it will report an exception of illegal thread status.

public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread thread = new Thread() {
            @Override
            public void run() {
                System.out.println("线程");
            }
        };
        thread.start();
        thread.start();
        System.out.println("hell main");
    }
}

Interrupt thread 

Interrupting a thread means ending the thread's run method in advance. There are two commonly used methods:

1. By introducing a tag

2. Through the interrupt() method

Introducing tags

Here, the flag is used to end the run method early. When the main thread executes flag = true, the other thread will end early.

public class ThreadDemo2 {
    public static  boolean flag = false;
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while(!flag) {
                    System.out.println("hell Thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
        thread.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("终止这个线程");
        flag = true;
    }
}

Note here that the flag identifier cannot be included in the main method and cannot be used as a local variable. Although the lambda anonymous inner class can access external local variables through variable capture, this local variable must be immutable and final modified. This conflicts with our need to terminate the thread by changing the mark, which is not feasible.

It must be final because the main method and Thread have their own function stack frames. Their life cycles are different. The flag is in the stack frame belonging to main. Once main is executed, its stack frame will be destroyed, and Thread wants to use it again. It is no longer needed. Variable capture here is born for this purpose. It is passing parameters, which essentially copies the flag on the required thread. In order to ensure the consistency of the flag, it cannot be changed.

interrupt()

Thread comes with a flag. We can obtain and change this flag through methods.

Here you can use Thread.interrupted() or Thread.currentThread().isInterrupted() to get the built-in flag bit.

The interrupt() method can change the flag bit.

Thread.currentThread().isterrupted is used here:

public class ThreadDemo3 {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while(!Thread.currentThread().isInterrupted()) {
                System.out.println("hell Thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        try {
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("中断它");
        thread.interrupt();

    }
}

But after running the code we will find the problem:

Here you will find that it will throw an interrupt exception and then continue running without stopping.

Reason for exception

At this time, because the interrupt() method here wakes up sleep in advance, sleep will do two things at this time:

1. Throw the InterruptedExecption exception

2. Restore the built-in flag bit.

All this causes the thread to continue running.

If the thread needs to stop, we can just add break in the catch.

Here we have three processing methods in catch:

1. Let the thread stop immediately

2. Let the thread run some code before stopping

3. Let the thread continue running without stopping

 We also have several common ways to handle exceptions:

Waiting program - join()

The join method can adjust the order of execution of threads. Although the scheduling execution of threads is randomly scheduled, join here can block the threads, which affects the order of execution of threads. The thread where join is located will be blocked. After calling the join method After the thread finishes running, the blocking state of this thread will be released.

Note: Here, the thread calling join() is waited for to be executed first, and the thread where join() is located waits and is executed later.

There are three join() methods:

The first type: Waiting to death, you need to wait for the waiting thread to finish executing before the blocking state is released.

The second type: Waiting for a certain period of time, blocking within a certain period of time. If the time range is exceeded, the blocking state will be released.

The third type: There is time to wait accurate to microseconds.

Under normal circumstances, the second case is what we use most:

public class ThreadDemo4 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            while(true) {
                System.out.println("hell main");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        thread.start();
        thread.join();
        while(true) {
            System.out.println("hell ");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

        }
    }
}

This code means that the main thread enters the blocking state and waits for the Thread thread to end. Although the Thread thread can be scheduled and executed together with multiple other threads during execution, because the main thread has been waiting, even if the Thread thread is running on the CPU Even if you switch multiple times, it will not affect the execution of this thread first.

Note here: Our interrupt method can wake up the join thread in advance.

Get the current thread reference - currentThread()

 When using the class inheritance Thread to create a thread method, we can use this to directly reference the object. But when using lambda/anonymous inner class/Runnable, this no longer points to the Thread object. At this time, we need to use currentThread() to obtain the Thread object reference. The method. 

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

Sleep the current thread - sleep() 

sleep can sleep the current thread for a certain period of time. This time can be set by yourself. But using it requires throwing an exception or try-catch.The sleep time here is due to thread scheduling. It is uncontrollable and will usually be greater than or equal to the set time.

It also has two methods:

Generally, the first one is used, and the second one is accurate to microseconds.

public class ThreadDemo8 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            try {
                Thread.sleep(1111);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("休眠1.111秒");
        });
        t.start();
    }
}

Guess you like

Origin blog.csdn.net/paperjie/article/details/134552957