Basic operations of Thread class (JAVA multi-threading)

Table of contents

Creation of threads (Thread class)

Some common constructors of the Thread class

Some common properties in Thread class:

getId():

isDaemon()

isAlive()

interrupt thread

the first method:

The second method:

thread wait

join()

join(waiting time)

Sleeping thread

Thread status


Threads are a concept in the operating system. The operating system kernel implements mechanisms such as threads and provides some APIs for external use.

The Thread class in JAVA further abstracts and encapsulates the API provided by the system, so if you want to use multi-threading, you cannot do without the Thread class.

Creation of threads (Thread class)

There are many ways to create threads in JAVA, here are a few briefly introduced.

Method 1: We write a class ourselves so that this class inherits from the Thread class, and then rewrite the run() method inside.

    class MeThread extends Thread {
        //必须要实现这个方法,此方法是新线程执行的入口方法(告诉线程应该做什么)
        @Override
        public void run() {
            System.out.println("这是新线程执行的任务");
        }
    }

    public static void main(String[] args) {
        //创建一个线程对象
        MeThread t = new MeThread();
        //调用系统API启动线程
        t.start();
    }

When we run the program, the printing operation in the run() method will be executed.

new A Thread class only creates a thread and does not call the system API to create a thread. Only when the start() method is called will the system API be called to create a thread in the system and start it. (It’s okay if you don’t understand it at this time, because it will be verified when the isAlive() method is introduced)

This rewritten run() method can be understood as a task. This method will be automatically called and executed when the thread starts, < /span>When the thread finishes executing the content in this method, the thread will be destroyed and cannot be awakened using the start() method again.

At this time, we have made some slight modifications to the above code in order to betterpresent the effect of multi-threaded concurrent programming.

    class MeThread extends Thread {
        //必须要重写这个方法,此方法是新线程执行的入口(告诉线程应该做什么)
        @Override
        public void run() {
            //此处让这个新线程每隔0.1s执行一次打印操作
            while (true) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("这是创建的新线程执行的任务");
            }
        }
    }

    public static void main(String[] args) {
        MeThread t = new MeThread();
        //调用系统API启动线程
        t.start();
        //让主线程每隔0.1s执行一次打印操作
        while (true) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("主线程");
        }
    }

5420a28a4bc04b979e145e656e12fa9c.png

At this time, you can see that the two while loops are "executed simultaneously", and each thread is an independent execution stream.

After the code is executed, you can see that the program is irregularlyprinting< a i=4>, the main reason is that the system’s thread scheduling is random


Method 2: We write a class ourselves to implement the Runnable interface and then rewrite the run() method inside.

  • The Thread class implements the Runnable interface;
  • The run() method in the Thread class is also overridden in the Runnable interface;
  • Because the Thread class provides such a constructor:57fd3e2dbd40417aa31283ca59e7d382.png

    class MeRunnable implements Runnable{
        //必须要重写这个方法,此方法是新线程执行的入口
        @Override
        public void run() {
            System.out.println("这是一个新线程执行的任务");
        }
    }

    public static void main(String[] args) {
        MeRunnable runnable = new MeRunnable();
        Thread t = new Thread(runnable);
        //调用系统API启动线程
        t.start();
    }

The difference between using the Runnable interface and directly inheriting Thread is that it can help us reduce the coupling of the code, that is, "decoupling".

Runnable represents an executable task and it does not care what the task is or where it is executed; this task is not necessarily strongly related to threads, because this code may It makes no difference whether you use single threads, multi-threads, or do not use threads or use other methods (for example: thread pool, coroutine...) to execute.

At this time, you can use Runnable to extract this task separately, so that you can change the method used to execute the task at any time (for example: if you do not want to use threads later You can call it directly in the main method) Example:

    class MeRunnable implements Runnable{
        @Override
        public void run() {
            System.out.println("这是一个任务");
        }
    }

    public static void main(String[] args) {
        MeRunnable runnable = new MeRunnable();
        //此时不想使用线程执行这个任务
        runnable.run();
    }

Method 3: Use anonymous inner classes.

    public static void main(String[] args) {
        Thread t = new Thread(){
            @Override
            public void run() {
                System.out.println("这是创建的新线程执行的任务");
            }
        };
        //调用系统API启动线程
        t.start();
    }

Because the Thread class provides such a constructor:57fd3e2dbd40417aa31283ca59e7d382.png

So we can write it like this: 

    public static void main(String[] args) {
        Thread t = new Thread(new Runnable(){
            @Override
            public void run() {
                System.out.println("这是一个新线程执行的任务");
            }
        });
        //调用系统API启动线程
        t.start();
    }

 Method 4: Because the Runnable interface is a functional interface, you can use Lambda expressions to create threads.

    public static void main(String[] args) {
        Thread t = new Thread(() -> System.out.println("这是创建的新线程执行的任务"));
        //调用系统API启动线程
        t.start();
    }

In addition to watching the console output to observe multi-threading, you can also use the tool jconsole included in the JDK to observe more vividly:For specific methods, you can jump here icon-default.png?t=N7T8https://blog.csdn.net/2302_76339343/article/details/133760752

Some common constructors of the Thread class

method illustrate
Thread() Create thread object
Thread(Runnable target) Create a thread object using a Runnable object
Thread(String name) Create a thread object and name it
Thread(Runnable target, String name) Create a thread object using a Runnable object and name it
Thread(ThreadGroup group,Runnable target) Threads can be managed in groups, and the groups are called thread groups.

Some common properties in Thread class:

Attributes Get method
ID getId()
name getName()
state getState()
priority getPriority()
Whether background thread isDaemon()
Is it alive? isAlive()
Whether it was interrupted isInterrupted()

getId():

Get the id of the current thread 

id is the unique identity of the thread. This id is assigned by JAVA to this thread. It is not an ID assigned by the system API, nor is it the ID of the PCB.

    public static void main(String[] args) {
        Thread t = new Thread(() -> {while (true) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                //打印异常
                e.printStackTrace();
            }
            System.out.println("这是创建的新线程执行的任务");
        }});

        Thread t1 = new Thread(() -> {while (true) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("这是创建的新线程执行的任务");
        }});
        t.start();
        t1.start();
        System.out.println(t.getId());//获取并打印当前线程的ID
        System.out.println(t1.getId());//获取并打印当前线程的ID
    }

b804bcb5dcb64387821b90b2bdf7d29e.png

isDaemon()

Determine whether the current thread is a background thread.

Threads are all foreground threads by default.

  • Foreground thread: As long as there is a foreground thread in the process that has not finished executing, the process will not end (the foreground thread will affect whether the process ends or not);
  • Background thread: As long as all foreground threads in the process are completed, the process will end regardless of whether there are unfinished background threads (background threads will not affect the end of the process).
    public static void main(String[] args) {
        Thread t = new Thread(() -> {while (true) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                //打印异常
                e.printStackTrace();
            }
            System.out.println("这是创建的新线程执行的任务");
        }});

        t.start();
        System.out.println(t.isDaemon());//获取并打印当前线程是否为后台线程
    }

0529f0a3cabe4a7cb3c1601bc1ed7ca9.png

At this time, change the above code to a background thread.

    public static void main(String[] args) {
        Thread t = new Thread(() -> {while (true) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                //打印异常
                e.printStackTrace();
            }
            System.out.println("这是创建的新线程执行的任务");
        }});
        //将当前线程设置为后台线程
        t.setDaemon(true);
        t.start();
        System.out.println(t.isDaemon());
    }

8bd1e4d9722f48f899f64e0cb9d291f1.png

At this time, because the main thread (main thread) finished executing quickly, there was no printing.

isAlive()

Print before and after the thread starts to determine whether the thread is alive (note: the thread does not necessarily survive when the thread object survives)

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                //打印异常
                e.printStackTrace();
            }
            System.out.println("新线程执行完毕");
        });
        System.out.println("线程启动前");
        System.out.println("线程是否存活"+t.isAlive());
        t.start();
        System.out.println("线程已启动");
        System.out.println("线程是否存活"+t.isAlive());
        Thread.sleep(2000);
        System.out.println("线程是否存活"+t.isAlive());
    }

c5b74a4e3d544c9299f1eab678344a41.png

According to the conclusion, we can knowWhen we create the thread object, we will not call the system API to create the thread. Only when the start() method is called, the system API will be called in the system. Create a thread and start it.

interrupt thread

The method of interrupting a thread in JAVA is relatively unique: essentially, it is to let the run() method finish executing as soon as possible; in C++, there is a way to interrupt the thread The thread is destroyed directly during execution, but there are disadvantages to this. For example, if the thread is suddenly interrupted while writing an article, the article will have no beginning and no end. In JAVA, some finishing work can be allowed here.

In reality, the reason why the run() method cannot be ended is generally a loop, so as long as the loop is ended, the thread can complete the run() method as soon as possible, thus achieving the effect of interrupting the thread.

Two methods are introduced here:

the first method:

You can manually create a flag to control the termination condition of the loop in the run() method.

    //创建一个成员变量用来控制循环的终止条件,默认值为 false;
    private static boolean isQuit;
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while(!isQuit) {
                System.out.println("新线程正在工作");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    //打印异常
                    e.printStackTrace();                }
            }
            System.out.println("新线程执行完毕");
        });
        
        t.start();
        Thread.sleep(500);
        
        isQuit = true;
        System.out.println("打断新线程");
    }

cf6542b5d12c4b8abd3f50a8b87e8445.png

But there are two problems:

  1. Flag bits need to be created manually;
  2. If the loop is in the sleep state, the program will not be able to respond in time.

The second method:

There is a flag bit by default in JAVA. We can use the default flag bit in JAVA to quickly end the run() method.

The advantage is that we no longer need to create a separate variable and no longer need to think about variable capture.

  • interrupt(): This method can set the default flag bit in the thread to true
  • isInterrupt(): Determines whether the flag bit of the thread associated with the object is set, and does not clear the flag bit after the call. You can also make the sleep() method throw an InterruptedException exception to forcibly interrupt the sleep() method.

You can use these two methods to interrupt threads

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            //Thread.currentThread()该方法是用来得到该线程的实例也就是t(哪个线程调用该方法就返回哪个线程的实例)
            //因为此时t还没有被创建所以不能写为t.isInterrupted()
            while(!Thread.currentThread().isInterrupted()) {
                System.out.println("新线程正在工作");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    //打印异常
                    e.printStackTrace();
                }
            }
            System.out.println("新线程执行完毕");
        });

        t.start();
        Thread.sleep(5000);
        //设置标志位为true
        t.interrupt();
        System.out.println("打断新线程");
    }

0a7d1cbb2cb043519d03f502c3c38b12.png

The results show that the flag is indeed set at this time, and the sleep() method also throws an exception, but the loop is not terminated.

The reason is that the sleep() method will automatically clear the flag after throwing an exception, and the result is the same as not setting the flag.

The reason why JAVA is designed in this way is actually to expand the programmer's operational space, and can perform some finishing work after the sleep() method throws an exception.

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            //Thread.currentThread()该方法是用来得到该线程的实例也就是t(哪个线程调用该方法就返回哪个线程的实例)
            //因为此时t还没有被创建所以不能写为t.isInterrupted()
            while(!Thread.currentThread().isInterrupted()) {
                System.out.println("新线程正在工作");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    //此处可以写一些收尾工作的代码
                    break;
                }
            }
            System.out.println("新线程执行完毕");
        });

        t.start();
        Thread.sleep(500);
        //设置标志位为true
        t.interrupt();
        System.out.println("打断新线程");
    }

37fd158250634d449c15b256f84593f7.png

thread wait

In multi-threaded code, since thread scheduling is random, the end time of each thread will also be unpredictable. In this case, BUG will appear in the code in some scenarios.

Thread waiting is to let one thread wait for the completion of another thread's execution. is essentially to control the order in which threads end.

join()

Realize the effect of thread waiting, allowing one thread to block and wait for another thread to finish executing before executing.

  • Waiting thread: In which thread the join method is called, that thread will block and wait;
  • The thread being waited for: Which thread object's join method is called, which thread is the thread being waited for, and the waiting thread will only be executed when this thread completes execution.

We create a thread t, let this thread print data every second, and let the main thread wait for this thread.

Thread t = new Thread(()->{
    for (int i = 0; i < 4; i++) {
        System.out.println("t线程执行中");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
});
t.start();
System.out.println("等待开始");
t.join();
System.out.println("等待结束");

2c3946c9d755441484d21364a57922f3.png

Note: If the execution of thread t has ended, calling join at this time will directly return to execution, and no blocking wait will occur.

join(waiting time)

The join() method above is a "wait to death" method. As long as the thread being waited for does not end, it will wait forever.

But under normal circumstances, we do not wait to death, but after waiting for more than a certain period of time, we will no longer wait, because it is meaningless.

  • join(long millis) Wait at most millis milliseconds
  • join(long millis, int nanos) is the same as the above method, except that the time accuracy is higher, accurate to nanoseconds

Sleeping thread

  • sleep(long millis) Let the thread sleep for millis milliseconds;
  • sleep(long millis, int nanos) has the same function as above, but with higher precision.

But the following one actually doesn't make much sense, because sleep() itself has a certain error. It doesn't mean that if you write sleep(1000), it will really wait for exactly 1000 ms. It also has a scheduling overhead. The system will sleep the thread according to the time of 1000. When the time is up, the system will wake up the thread (blocked -> ready), and the thread does not enter the ready state to immediately enter the CPU execution.

//获取系统当前时间戳
long a = System.currentTimeMillis();
Thread.sleep(1000);
//获取系统当前时间戳
long b = System.currentTimeMillis();
System.out.println("时间:"+(b - a)+" ms");

52b3dd8f39f849abb5d11fc648f39e8e.png

And the results are different after each run.

54aa69af05034568b1a5dd85b80dc0af.png

Thread status

All states of threads in JAVA are stored in an enumeration type, Thread.State:

for (Thread.State str:Thread.State.values()) {
    System.out.println(str);
}

You can print all thread status through the above code

17e423c9afad444c9c7664b43406901b.png

The state of the thread can be obtained through the getState() method

  • NEW: The Thread object already exists, but the start() method has not been called yet;
Thread t = new Thread(()->{

});
System.out.println(t.getState());

84dd996330ca4313911fe67c34c5133a.png

  • RUNNABLE: Ready state (the thread has been executed on the CPU or is queued for execution)
Thread t = new Thread(()->{
    while(true) {

    }
});
t.start();
System.out.println(t.getState());

c88cff52f82c4364a25871db4bc8de2f.png

  • BLOCKED: blocking, blocking due to lock competition
Object lock1 = new Object();
Object lock2 = new Object();

Thread t1 = new Thread(()->{
    synchronized(lock1) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        synchronized(lock2) {

        }
    }
});
Thread t2 = new Thread(()->{
    synchronized(lock2) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        synchronized(lock1) {

        }
    }
});
t1.start();
t2.start();
//让主线程等待 2 秒
Thread.sleep(2000);
//此时t1和t2两个线程会因为互相争对方的锁,而导致死锁
System.out.println(t1.getState());
System.out.println(t2.getState());

1383895c2cb345689981e925ddcdd38c.png

  • WAITING: Blocking caused by wait, which is a non-fixed time method.
Object lock1 = new Object();

Thread t1 = new Thread(()->{
    synchronized(lock1) {
        try {
        //调用wait方法让线程阻塞
            lock1.wait();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
});

t1.start();
Thread.sleep(1000);
System.out.println(t1.getState());

bccdeb32a01840498dfaa1a882f319f4.png

  • TIMED_WAITING: Blocking caused by sleep, a fixed time limit method
Thread t1 = new Thread(()->{
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
});

t1.start();
Thread.sleep(1000);
System.out.println(t1.getState());

84ebca74d7724f518e8ccf56ce4c2564.png

  • TERMINATED: The Thread object is still there, but the thread is gone.
Thread t1 = new Thread(()->{

});

t1.start();
Thread.sleep(1000);
System.out.println(t1.getState());

a4d19a8accb640d3a5e29eabc93a7c6d.png

Guess you like

Origin blog.csdn.net/2302_76339343/article/details/133924126