Java Concurrent Programming (Ⅰ)


1. Concept

1. Basic concepts

Processes and threads:

  • Graphic:
  • letter:
    • Process: an instance of a program. A program can have multiple instances, as shown in the figure below.

      Now the QQ program has two processes.
    • Thread: A thread is the execution unit of a process, and there are multiple threads in a process
  • concept:
    • Process: A process is the basic unit for allocating and managing resources , and different processes compete for computer system resources
    • Thread: thread is the smallest scheduling unit (the basic unit of processor scheduling)
  • the difference:
    • Independent | Shared
      • Threads share the address space and resources of the process (memory, CPU, IO, etc.)
      • Each process has an independent address space and independent resources
    • robust?
      • After one thread crashes, the whole process will crash
      • After a process crashes, it will not affect other processes in protected mode
    • overhead?
      • The thread is attached to the process to run, obviously the process overhead is high (including startup overhead, switching overhead)
    • communication
      • The process communication of the same computer is called IPC (Inter-process communication) . The process communication between different computers needs to go through the network and follow a common protocol, such as HTTP
      • Because threads share process resources, communication is very simple, such as sharing a static resource

Concurrency and parallelism:
A single-core CPU can only execute one thread at a time, that is, serial execution . The task scheduler in the operating system allocates CPU time slices (very short) to different threads. Because the CPU switches between threads very quickly, it will give people the illusion that multiple threads are running at the same time.

  • Concurrent: the ability to deal with multiple things at the same time
  • Parallel: the ability to do multiple things at the same time

Synchronous and asynchronous:

  • Synchronization: that is, serial execution, you need to wait for the previous code to return before continuing to execute the subsequent code.

    @Slf4j
    public class ShowSync {
          
          
    
        public static void count(int i) {
          
          
            log.info("第" + i + "次执行count方法");
        }
    
        public static void main(String[] args) {
          
          
            for (int i = 0; i < 3; i++) {
          
          
                count(i);
            }
            log.info("线程:" + Thread.currentThread().getName());
        }
    }
    
  • Asynchronous: The following code can be executed without waiting for the previous code to return.

    @Slf4j
    public class ShowAsync {
          
          
        public static void main(String[] args) {
          
          
            for (int i = 0; i < 3; i++) {
          
          
                new Thread(() ->
                    log.info("线程:" + Thread.currentThread().getName())
                ).start();
            }
            log.info("线程:" + Thread.currentThread().getName());
        }
    }
    

2. The state of the thread

An enumeration class State is provided in the Thread class , as follows:

public enum State {
    
    

        NEW,
        
        RUNNABLE,

        BLOCKED,

        WAITING,

        TIMED_WAITING,

        TERMINATED;
    }

It can be seen that a Java thread has six states . But in fact, we will divide RUNNABLE into two states: Ready and RUNNABLE , as shown in the figure below, so we generally say: a Java thread has seven states .

  • NEW
    • Thread t1 = new Thread() enters this state
  • READY
    • t.start() enters this state
    • At this point, the thread can already run, but has not yet obtained the CPU time slice, and is in the ready state
  • RUNNING
    • The ready thread gets the CPU time slice and enters the running state
  • BLOCKED
    • The thread enters the synchronized modified method or code block
  • TERMINATED
    • The thread's run() has finished running or main() has finished running
  • WAITING
    • Threads entering this state will not be allocated CPU time slices and need to be woken up by other threads, otherwise they will wait forever
  • TIMED_WAITING
    • Threads entering this state will not be allocated CPU time slices, do not need to be woken up by other threads, and will automatically wake up after a certain period of time

As for the state transition and related methods in the thread, I will introduce it later.

2. Thread initialization

Thread initialization, that is, new Thread() , we have three ways to achieve initialization, namely:

  • Direct new Thread() and then rewrite run()
  • new Thread(new Runnable()) , assemble Runnable 's run() into Thread
  • new Thread(new FutureTask(new Callable())) , assemble call() in Callable to FutureTask first and then to Thread

1. new Thread()

Directly new Thread() to initialize the thread is to bind the thread (Thread) and the task (run()) together. It is not recommended to use it in actual development. The code is as follows:

@Slf4j
public class JustThread {
    
    
    public static void main(String[] args) {
    
    
        Thread thread1 = new Thread("线程1") {
    
    
            @Override
            public void run() {
    
    
                log.info("线程:" + Thread.currentThread().getName() + " 运行中...");
            }
        };
        thread1.start();

        log.info("线程:" + Thread.currentThread().getName() + " 运行中...");

        /*
            lambda表达式
         */
        Thread thread2 = new Thread(() -> {
    
    
            log.info("线程:" + Thread.currentThread().getName() + " 运行中...");
        }, "线程2");
        thread2.start();
    }
}

2. new Thread(new Runnable())

Initialize threads (Thread) and tasks (Runnable) separately, and then combine them together.

@Slf4j
public class RunnableAndThread {
    
    
    public static void main(String[] args) {
    
    
        Runnable runnable = new Runnable() {
    
    
            @Override
            public void run() {
    
    
                log.info("线程:" + Thread.currentThread().getName() + " 运行中...");
            }
        };
        Thread thread = new Thread(runnable, "线程3");
        thread.start();

        /*
            lambda表达式
         */
        Runnable runnable1 = () -> log.info("线程:" + Thread.currentThread().getName() + " 运行中...");
        Thread thread1 = new Thread(runnable1, "线程4");
        thread1.start();
    }
}

3. The relationship between Thread and Runnable

First look at the source code of Runnable , as follows:

@FunctionalInterface
public interface Runnable {
    
    
    public abstract void run();
}

A functional interface with only one run() .

Then look at part of the source code of Thread :

    @Override
    public void run() {
    
    
        if (target != null) {
    
    
            target.run();
        }
    }

    public Thread(Runnable target) {
    
    
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
    
    
        init(g, target, name, stackSize, null, true);
    }

Okay, just look at these few paragraphs of methods, and I won’t do an in-depth analysis here. From the above code, we can clearly understand the relationship between Thread and Runnable :

  • When new Thread , if no Runnable is passed in , Thread 's run() will be rewritten
  • In new Thread , if Runnable is passed in , run() in Runnable will be used

Therefore, the Runnable interface is to provide a method body run() for Thread .

In actual development, we usually customize a class to implements Runnable , and then pass this class into Thread . The case is as follows:

  • Define a thread that prints the current time every ten seconds
public class TimePrinter implements Runnable{
    
    
    private SimpleDateFormat dateFormat= new SimpleDateFormat("hh:mm:ss");

    @Override
    public void run() {
    
    
        while (true) {
    
    
            try {
    
    
                System.out.println("当前时间为: " + dateFormat.format(new Date()));
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
    
    
                throw new RuntimeException(e);
            }
        }
    }
}
public class TaskDemo {
    
    
    public static void main(String[] args) {
    
    
        Thread thread = new Thread(new TimePrinter(), "测试线程");
        thread.start();
    }
}

4. new Thread(new FutureTask(new Callable()))

@Slf4j
public class FutureTaskAndThread {
    
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    


        FutureTask<Long> futureTask = new FutureTask<>(new Callable<Long>() {
    
    
            @Override
            public Long call() throws Exception {
    
    
                long start = System.currentTimeMillis();
                log.info(Thread.currentThread().getName() + " 运行中...");
                TimeUnit.MILLISECONDS.sleep(123);
                long end = System.currentTimeMillis();
                return end - start;
            }
        });
        Thread thread = new Thread(futureTask, "线程5");
        thread.start();
        log.info(thread.getName() + "花费了 " + futureTask.get() + " 毫秒");
    }
}

The relationship between Runnable and Callable is as follows:
It can be seen that compared with Runnable 's run() , Callable 's call() has one more return value.

@FunctionalInterface
public interface Callable<V> {
    
    
    V call() throws Exception;
}

3. Common methods

Switching of thread state:

The difference between TIMED_WAITING and WAITING:

  • TIMED_WAITING Even if there is no external signal, the thread will resume after the waiting time expires
  • WAITING needs to wait for an external signal to wake up

1. start

First look at the source code:

	// 线程组
	private ThreadGroup group;
	// NEW 状态的线程的 threadStatus = 0
	private volatile int threadStatus = 0;

    public synchronized void start() {
    
    

        if (threadStatus != 0)
            throw new IllegalThreadStateException();
		// 将要启动(start)的线程装入线程组中
        group.add(this);

        boolean started = false;
        try {
    
    
            start0();
            started = true;
        } finally {
    
    
            try {
    
    
                if (!started) {
    
    
                	// 告诉组,这个线程启动(start)失败
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
    
    

            }
        }
    }

	// native方法,调用本地操作系统开启一个新的线程
    private native void start0();

After reading the source code, the following conclusions can be drawn:

  • start to start a thread needs to go through the following steps:
    1. Load the thread calling start into the thread group ThreadGroup
    2. Call the native method to start the thread

1. Thread group

Reference link here: https://zhuanlan.zhihu.com/p/61331974

Thread Group (Thread Group): A collection of threads that can easily manage threads in a group.

Thread group tree:

  • System thread group : A thread group that handles JVM system tasks, such as object destruction, etc.

  • main thread group : contains at least one thread - main (used to execute the main method)

  • Child thread group of face thread group: the thread group created by the application

        public static void main(String[] args) {
          
          
            // 输出当前线程组——main线程组
            System.out.println(Thread.currentThread().getThreadGroup().getName());
            // 输出当前线程组的父线程组——System
            System.out.println(Thread.currentThread().getThreadGroup().getParent().getName());
        }
    

Assign a thread group to a thread:

  • If you do not specify an array when initializing a thread, it will be specified as the main thread group by default . The code is as follows:

    private void init(ThreadGroup g, Runnable target, String name,
                          long stackSize, AccessControlContext acc,
                          boolean inheritThreadLocals) {
          
          
                          
            /*
    			其余代码省略
    		*/
            Thread parent = currentThread();
            SecurityManager security = System.getSecurityManager();
            if (g == null) {
          
          
              
                if (security != null) {
          
          
                    g = security.getThreadGroup();
                }
    
                if (g == null) {
          
          
                    g = parent.getThreadGroup();
                }
            }
    
            /*
    			其余代码省略
    		*/
    }
    
  • A thread group can be assigned to a thread using any of the following constructors:

        public Thread(ThreadGroup group, Runnable target)
    
        public Thread(ThreadGroup group, Runnable target) 
    
    	public Thread(ThreadGroup group, Runnable target, String name)
    
        public Thread(ThreadGroup group, Runnable target, String name, long stackSize)
    

    Demonstrate it:

        public static void main(String[] args) {
          
          
            ThreadGroup group = new ThreadGroup("我的线程组");
            Thread thread = new Thread(group, "我的线程");
            System.out.println(thread.getName() + " 的线程组是 " + group.getName());
        }
    

Thread group method analysis: https://blog.csdn.net/a1064072510/article/details/87455525

To sum up: the purpose of loading threads into a thread group is to facilitate batch operation of threads. For example, if I want to notify several threads that they are about to terminate, I can put these threads into a thread group in advance, and then directly notify them in batches through the thread group:

// 通知main线程组中的所有线程要终止了
Thread.currentThread().getThreadGroup().interrupt();

2. start and run

Question: Why can start start a thread but not run ?

Answer: Because Java cannot directly act on the operating system, it is necessary to call native methods (such as methods written in C, C++, etc.) to tell the operating system to start a new thread. In the start method, the native method start0 is called to start the thread. As for run , this is a method in the thread. When the thread runs, it will execute run . When the run method is executed, the thread will end.

2. sleep

First look at the source code of Thread.sleep :

	/**
	*让当前线程睡眠指定毫秒数,线程不会丢失任何monitors的所有权
	*/
    public static native void sleep(long millis) throws InterruptedException;

Ok, let's use it now:

@Slf4j
public class Sleep_md{
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Thread t1 = new Thread(()->{
    
    
            long start = System.currentTimeMillis();
            log.info("t1睡眠5s");
            try {
    
    
                Thread.sleep(5 * 1000);
            } catch (InterruptedException e) {
    
    
            }
            long end = System.currentTimeMillis();
            log.info("已过去 {} 毫秒", (end - start));
        });
        t1.start();
    }
}

Through the operation of the above code, it can be found that Thread.sleep can make the current thread enter TIMED_WAITING sleep for a period of time, and then enter RUNNABLE . During this period, the CPU will be released to give other threads a chance.

1. TimeUtil

In addition to Thread.sleep , TimeUtil also has sleep , which can make threads go to sleep, and both have the same effect.

    public void sleep(long timeout) throws InterruptedException {
    
    
        if (timeout > 0) {
    
    
            long ms = toMillis(timeout);
            int ns = excessNanos(timeout, ms);
            Thread.sleep(ms, ns);
        }
    }

Instructions:

// 睡眠n纳秒
TimeUtil.NANOSECONDS.sleep(long n)
// 睡眠n微秒
TimeUtil.MICROSECONDS.sleep(long n)
// 睡眠n毫秒
TimeUtil.MILLISECONDS.sleep(long n)
// 睡眠n秒
TimeUtil.SECONDS.sleep(long n)
// 睡眠n分钟
TimeUtil.MINUTES.sleep(long n)
// 睡眠n小时
TimeUtil.HOURS.sleep(long n)
// 睡眠n天
TimeUtil.DAYS.sleep(long n)

2. InterruptedException

InterruptedException is thrown when the sleeping thread is interrupted from sleeping .

@Slf4j
public class Sleep_md{
    
    
    public static void main(String[] args) {
    
    
        Thread thread = new Thread(() -> {
    
    
            log.info("开始睡眠");
            long start = System.currentTimeMillis();
            try {
    
    
            	// 睡眠 60s
                Thread.sleep(1000 * 60);
            } catch (InterruptedException e) {
    
    
                long end = System.currentTimeMillis();
                log.info("中途停止睡眠");
            }
        });
        thread.start();
        thread.interrupt();
    }
}

Visible to the naked eye, interrupt() interrupts the sleep, throws an exception, and is caught.

3. setPriority

setPriority() —— Set the thread priority, the value is from 1 — 10, the smaller the value, the higher the priority.
Note: A higher priority indicates that there is more opportunity to compete for the CPU time slice, not that the higher the priority, the faster it must be executed.

  • If the CPU is busy, threads with higher priority will get more time slices
  • When the cpu is idle, the priority has almost no effect
@Slf4j
public class Priority_md {
    
    
    public static void main(String[] args) {
    
    
        Thread t1 = new Thread(() -> log.info("t1运行中..."), "t1");
        t1.setPriority(Thread.MAX_PRIORITY);
        t1.start();
        Thread t2 = new Thread(() -> log.info("t2运行中..."), "t2");
        t2.setPriority(Thread.MIN_PRIORITY);
        t2.start();
        log.info("main运行中...");
    }
}
  • 3 constants

        public final static int MIN_PRIORITY = 1;
        public final static int NORM_PRIORITY = 5;
        public final static int MAX_PRIORITY = 10;
    
  • When setting the priority, if the value is not in the range [1,10], an exception IllegalArgumentException will be thrown

  • The priority of the main thread is 5, and the initial value of the priority of the custom thread is also 5

    Thread main = Thread.currentThread();
    log.info("main线程的优先级是: " + main.getPriority());
    

4. yield

Thread.yield() —— Prompt the thread scheduler that the current thread is willing to give up the CPU time slice to return to the ready state, and the thread scheduler can choose to ignore the prompt (may fail to yield).

Notice:

  • Giving up the time slice does not necessarily succeed, it all depends on the thread scheduler
  • The time slice given up may not be grabbed by the high-priority thread, because when the CPU is not tense, the priority is useless. Even if the CPU is relatively tight, it only improves the ability of the high-priority thread to grab the time slice.
@Slf4j
public class Yield_md {
    
    
    public static void main(String[] args) {
    
    
        Thread t1 = new Thread(new Runner(), "张三");
        t1.setPriority(Thread.MIN_PRIORITY);
        Thread t2 = new Thread(new Runner(), "李四");
        t2.setPriority(Thread.MIN_PRIORITY);
        Thread t3 = new Thread(new Runner(), "王五");
        t3.setPriority(Thread.MIN_PRIORITY);

        for (int i = 1; i < 10; i++) {
    
    
            Thread thread = new Thread(new Runner(), "路人" + i);
            thread.setPriority(Thread.MAX_PRIORITY);
            thread.start();
        }

        t1.start();
        t2.start();
        t3.start();
    }

    private static class Runner implements Runnable {
    
    

        @Override
        public void run() {
    
    
            for (int i = 1; i < 8; i++) {
    
    
                if (i % 4 == 0) {
    
    
                    log.info(Thread.currentThread().getName() + " 摔了一跤!");
                    Thread.yield();
                }
                if (i == 7) {
    
    
                    log.info(Thread.currentThread().getName() + " 完成了比赛!");
                }
            }
        }
    }
}

yield vs. sleep:

  • After the yield is successful, the thread enters READY directly , and sleep enters WAITING or TIMED_WAITING before entering. Therefore, the thread of yield may regain the CPU to continue running at any time, while sleep needs to wait for the end of the sleep time

5. join

join() joins the specified thread A to the currently running thread B, and thread B will enter the waiting state and wait for A to finish running before executing B.

@Slf4j
public class Join_md {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Thread t1 = new Thread(() -> {
    
    
            try {
    
    
                for (int i = 0; i < 3; i++) {
    
    
                    log.info("t1----->" + i);
                }
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                throw new RuntimeException(e);
            }
        }, "t1");
        Thread t2 = new Thread(() -> {
    
    
            try {
    
    
                t1.join();
                for (int i = 0; i < 3; i++) {
    
    
                    log.info("t2----->" + i);
                }
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                throw new RuntimeException(e);
            }
        }, "t2");
        long start = System.currentTimeMillis();
        t1.start();
        t2.start();

        try {
    
    
            t2.join();
        } catch (InterruptedException e) {
    
    
            throw new RuntimeException(e);
        }
        long end = System.currentTimeMillis();
        log.debug("t1: {} t2: {} cost: {}", t1, t2, end - start);
    }
}

Notice:

  • Join is equivalent to letting two alternate execution threads execute sequentially

  • When multiple threads (such as A, B, and C) join in a thread D , A, B, and C will be executed alternately, as follows:

    @Slf4j
    public class Join_md {
          
          
        public static void main(String[] args) throws InterruptedException {
          
          
            Thread t1 = new Thread(() -> {
          
          
                try {
          
          
                    log.info("t1......");
                    for (int i = 0; i < 5; i++) {
          
          
                        log.info("t1: " + i);
                    }
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
          
          
                    throw new RuntimeException(e);
                }
            }, "t1");
            Thread t2 = new Thread(() -> {
          
          
                try {
          
          
                    log.info("t2......");
                    for (int i = 0; i < 5; i++) {
          
          
                        log.info("t2: " + i);
                    }
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
          
          
                    throw new RuntimeException(e);
                }
            }, "t2");
            long start = System.currentTimeMillis();
            t1.start();
            t2.start();
    
            log.info("main...");
    
            try {
          
          
                t1.join();
                t2.join();
            } catch (InterruptedException e) {
          
          
                throw new RuntimeException(e);
            }
            long end = System.currentTimeMillis();
            log.debug("t1: {} t2: {} cost: {}", t1, t2, end - start);
        }
    }
    

join vs. yield:

  • From the function point of view: join blocks the current thread and let other threads execute first; yield makes the current thread give up its position and let other threads execute
  • From the status point of view: join will make the current thread enter WAITING ; yield will make the current thread enter READY
  • From the effect point of view: join will not fail; yield may let resources fail

6. interrupt

1. Introduction to the method

interrupt only changes the interrupt status of a thread ( Just to set the interrupt flag ), and notifies that thread, it does not interrupt a running thread.

isInterrupted determines the interrupt status of the current thread.

The isInterrupted of the current thread called by Thread.interrupted resets the interrupt status of the current thread to true.
After interrupt, the thread isInterrupted returns true, and Thread.interrupted resets the state to false.

@Slf4j
public class Interrupt_md {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Thread t1 = new Thread(() -> {
    
    
            if (Thread.currentThread().isInterrupted()) {
    
    
                log.info("t1已经被interrupt......");
                log.info("重置t1中断状态...");
                Thread.interrupted();
                if (!Thread.currentThread().isInterrupted()) {
    
    
                    log.info("t1状态恢复...");
                }
            } else {
    
    
                log.info("t1还没被interrupt");
            }
        });
        t1.start();
        t1.interrupt();
    }
}

Interrupted by interrupt during the execution of the following methods will throws InterruptedException : Thread.sleep , join , wait .

2. Interrupt thread

Judging the flag bit, return interrupt interrupting a thread is the run
method that ends its operation , so just return directly .

  • Use isInterrupted as a flag

    @Slf4j
    public class StopThread {
          
          
        public static void main(String[] args) {
          
          
            Thread t1 = new Thread(() -> {
          
          
               if (Thread.currentThread().isInterrupted()) {
          
          
                   return;
               }
               for (;;) {
          
          
                   log.info("t1 is running...");
               }
            });
            t1.start();
            t1.interrupt();
        }
    }
    
  • Customize a flag

    @Slf4j
    public class StopThread {
          
          
        private volatile static boolean flag = false;
    
        public static void main(String[] args) {
          
          
            Thread t1 = new Thread(() -> {
          
          
               if (flag) {
          
          
                   return;
               }
               for (;;) {
          
          
                   log.info("t1 is running...");
               }
            });
            t1.start();
            t1.interrupt();
            flag = true;
        }
    }
    

7. park

The method of LockSupport.park to make the thread sleep (entering the WAITING state), will not directly enter the sleep state after calling, and will first judge according to a permit . If the permit exists, it will return directly, and if it does not exist, it will enter the sleep state ( Disables the current thread for thread scheduling purposes unless the permit is available. If the permit is available then it is consumed and the call returns immediately; otherwise the current thread becomes disabled for thread scheduling purposes )

There are three situations that break Hibernation:

  • Another thread calls the LockSupport.unpark method
  • call interrupt
  • other reasons

LockSupport.parkNanos Compared with LockSupport.park , LockSupport.park needs to specify a time, and then enter the sleep state (TIMED_WAITING), no need to wake up, it will wake up naturally when the time is up.

@Slf4j
public class Park_demo {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Thread t1 = new Thread(() -> LockSupport.park(), "t1");
        Thread t2 = new Thread(() -> LockSupport.parkNanos(1000*1000*3000L), "t2");
        t1.start();
        t2.start();
        TimeUnit.SECONDS.sleep(1);
        log.info("t1's state: {}, t2's state: {}", t1.getState(), t2.getState());
    }
}

Notice:

  • LockSupport.parkNanos will not throw an exception, so using interrupt to interrupt during sleep will not throw an exception

  • LockSupport.unpark can be used to wake up sleeping threads

    @Slf4j
    public class Park_demo {
          
          
        public static void main(String[] args) throws InterruptedException {
          
          
            Thread t1 = new Thread(() -> {
          
          
                LockSupport.park();
                log.info("t1苏醒...");
            }, "t1");
            t1.start();
            TimeUnit.SECONDS.sleep(1);
            log.info("t1's state: {}, t1's status: {}", t1.getState(), t1.isInterrupted());
            LockSupport.unpark(t1);
            log.info("t1's state: {}, t1's status: {}", t1.getState(), t1.isInterrupted());
        }
    }
    

8. setDaemon

Reference article in this section: https://www.cnblogs.com/lixuan1998/p/6937986.html

setDaemon(true) sets the thread as a daemon thread.

Java thread classification:

  • user thread
  • Daemon thread
    is a thread that provides a general service in the background when the program is running. For example, the garbage collection thread is a very competent guardian, and this thread is not an integral part of the program. Therefore, when all non-daemon threads end, the program terminates, killing all daemon threads in the process. Conversely, as long as any non-daemon threads are still running, the program will not terminate. The most typical daemon thread: garbage collection thread.
  • The difference between the two: there is no essential difference between the daemon thread and the user thread: the only difference lies in the departure of the virtual machine: if the user threads have all exited and only the daemon thread exists, the virtual machine will also exit up. Because there is no guardian, the daemon thread has no work to do, and there is no need to continue running the program.

Notes on using daemon threads:

  • setDaemon(true) must be used before start , otherwise an exception IllegalThreadStateException will be thrown
  • The new thread generated in the daemon thread is also a daemon thread
  • A daemon thread should never access inherent resources such as files, databases, because it can be interrupted at any time even in the middle of an operation
@Slf4j
public class Daemon_md {
    
    
    public static void main(String[] args) {
    
    
        Thread t1 = new Thread(() -> log.info("t1.isDaemon: {}", Thread.currentThread().isDaemon()), "t1");
        Thread t2 = new Thread(()->{
    
    
            t1.start();
            Thread t3 = new Thread("t3");
            t3.start();
            log.info("t2.isDaemon: {}", Thread.currentThread().isDaemon());
            log.info("t3.isDaemon: {}", t3.isDaemon());
        }, "t2");
        t2.setDaemon(true);
        t2.start();
        log.info("main.isDaemon: {}", Thread.currentThread().isDaemon());
    }
}

Guess you like

Origin blog.csdn.net/m0_54355172/article/details/128712835