This is the seventh day of my participation in the August More text Challenge. For details, see:August is more challenging

Thread concept

Threads are the basic scheduling unit of the CPU. A process is composed of multiple threads. Threads actually function. The main method in Java, for example, starts a thread to execute the code, a single flow of execution

Simply understand, a QQ client is a process, a session window is a thread. If multiple session Windows are opened on a client, multiple threads are started

In the case of thread concurrency, multiple threads are executing in the same period of time. Multiple threads execute code simultaneously, which can improve the efficiency of the program. It is worth noting that the number of threads in the CPU is limited, and the number of threads that are exceeded no longer works, such as the four-core eight-thread computer hype, and the creation of a ninth thread is invalid

Next, the creation, startup, state, and so on of threads are described

Thread creation

There is a big debate about how to create threads in Java. A variety of? Each party said

The first is to inherit the Thread class

public static void main(String[] args) {
    new B().start();
}

public static class B extends Thread {
    @Override
    public void run(a) {
        System.out.println("The thread executed..."); }}Copy the code

This is the first way to create a Thread. It extends the Thread class directly and overrides the run method. As you can see, there are drawbacks to this approach. Java is single inheritance, multiple implementations

Second: implement the Runnable interface

public static void main(String[] args) {
    new Thread(new B()).start();
}

public static class B implements Runnable {
    @Override
    public void run(a) {
        System.out.println("The thread executed..."); }}Copy the code

Implement the Runnable interface, which is the most common way to create threads, avoiding the pitfalls of inherited creation. The start method is wrapped in the Thread object

The third is to implement the Callable interface

public static void main(String[] args) {
    FutureTask task = new FutureTask<>(new B());
    new Thread(task).start();
    try {
        System.out.println(task.get());
    } catch(InterruptedException | ExecutionException e) { e.printStackTrace(); }}public static class B implements Callable {
    @Override
    public Object call(a) throws Exception {
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }
        return "Data returned"; }}Copy the code

Again, you need to pay attention to the differences in writing. Threads created in this way have return values, can throw exceptions, and can be used as needed

In addition, there is a thread creation method, thread pool creation. Regardless of the difference, just know it (the IDE reminds you)

public static void main(String[] args) {
    ExecutorService threadPool = Executors.newFixedThreadPool(5);
    threadPool.execute(new B());
    threadPool.shutdown();
}

public static class B implements Runnable {
    @Override
    public void run(a) {
        Thread.currentThread().setName("Thread B"); System.out.println(Thread.currentThread().getName()); }}Copy the code

As you can see, objects in the thread pool do not need the start method to execute the thread. Importantly, thread pools do allow for the creation of thread objects

The question, then, is how many ways threads can be created in Java. The answer is, one!

That’s the conclusion I got when I was reading a big guy’s blog. In essence, the creation of threads depends on the Thread class

Thread start

For the start of a thread object, the actual method of execution is start, not run. Start indicates that a thread is started and the CPU allocates resources for it. When you run, you call the run method on the thread object, not start a thread

Threads have some built-in properties, such as thread ID and name. You can tell if the threads are the same by the differences in the built-in properties

public static void main(String[] args) throws InterruptedException {
    new B().start();
    Thread.sleep(3000);
    new B().start();
    Thread.sleep(3000);
    new B().start();
}

public static class B extends Thread {
    @Override
    public void run(a) { System.out.println(Thread.currentThread().getId()); System.out.println(Thread.currentThread().getName()); }}Copy the code

In threads, there is also the concept of thread priority. In Java, the priority is from small to large, from 1 to 10. For priorities, this is a soft recommendation for the CPU, and it is not surprising that the lowest priority thread executes before the highest priority thread. CPU operation, no human interference

public final void setPriority(int newPriority)
public final int getPriority(a)
Copy the code

Thread state

The life cycle of a thread can be roughly divided into five states: new, ready, running, blocked, and ended

Inside the Thread class, there is an enumeration class State, which defines the various states of the Thread

public enum State {
    NEW,
    RUNNABLE,
    BLOCKED,
    WAITING,
    TIMED_WAITING,
    TERMINATED;
}
Copy the code

Thread Thread = NEW Thread(); Thread = NEW Thread();

Ready: The Thread enters the ready state by calling the start() method of Thread

  • At this point, the thread, having acquired all the resources except the time slice, is just waiting for the CPU to allocate resources

Run RUNNABLE: The thread gains access to the CPU and the run() method starts executing

  • There are three possibilities for a running thread: thread blocking, thread conciliation, and thread ending

BLOCKED: The thread enters a BLOCKED state, also called a thread hang

  • sleep(): The thread is hibernated. The current thread is blocked. The hibernation time must be set
  • join(): A thread joins, and other threads block until the current thread finishes executing
  • wait(): The thread is waiting. The current thread is blocked. You can set the waiting time

For thread blocking, according to the different methods called when blocking, can be divided into

  1. WAITING: The thread is WAITING and the time is not set

    • public final void join() throws InterruptedException {}
    • public final void wait() throws InterruptedException {}
  2. TIMED_WAITING: specifies the time limit for waiting when the time limit is exceeded

    • public final native void wait(long timeoutMillis) throws InterruptedException;
    • public static native void sleep(long millis) throws InterruptedException;
    • public final synchronized void join(long millis) throws InterruptedException {}

When a thread changes from the blocked state to the ready state, it is a thread wake, but there is a possibility of false wake

  • notify(): Randomly wakes up a thread blocked by wait()
  • notifyAll(): Wakes up all threads blocked by wait()

If the blocked thread is stopped due to interruption or timeout, it is called false wake. You need to set a judgment condition

Comity: The running thread gives up the CPU usage and enters the ready state

  • Yield () : The current thread gives up the CPU usage and becomes ready

TERMINATED: the thread is TERMINATED when the run method of the thread finishes running

  • Stop () : Forcibly terminates the current thread. This command is not recommended

It is worth noting that there is a clear difference between thread blocking and comity

  1. The thread is blocked, not directly in the ready state
  2. Thread comity, the thread enters the ready state, there is the possibility of obtaining the CPU right again immediately
  3. The CPU can choose to receive or reject a courtesy request from a thread. It is similar to the priority of the thread and is not hard

Thread the interrupt

The interruption of a thread, which is a way of collaboration between threads, is blocked, freeing the CPU

  • void interrupt(): sets the interrupt flag for this thread
  • static boolean interrupt(): Determines whether the thread is interrupted and removes the interrupt flag
  • boolean isInterrupt(): Determines whether the thread is interrupted and does not remove the interrupt flag
public static void main(String[] args) throws InterruptedException {
    ThreadA demo1 = new ThreadA();
    ThreadB demo2 = new ThreadB();
    demo1.setName("Thread A");
    demo2.setName("Thread B");
    demo1.start();
    demo2.start();
    Thread.sleep(1);
    demo1.interrupt();
}

static class ThreadA extends Thread {
    @Override
    public void run(a) {
        for (int i = 0; i < 100; i++) {
            if (ThreadA.interrupted()) {
                try {
                    System.out.println("Thread break, thread sleep.");
                    ThreadA.sleep(3000);
                } catch(InterruptedException e) { e.printStackTrace(); }}else {
                System.out.println(The current thread is:+ Thread.currentThread().getName()); }}}}static class ThreadB extends Thread {
    @Override
    public void run(a) {
        for (int i = 0; i < 100; i++) {
            if (ThreadA.interrupted()) {
                System.out.println("Thread break, thread sleep.");
                try {
                    ThreadA.sleep(3000);
                } catch(InterruptedException e) { e.printStackTrace(); }}else {
                System.out.println(The current thread is:+ Thread.currentThread().getName()); }}}}Copy the code

There are two things to note about thread interruptions

  1. isInterrupt()It simply determines whether the thread has an interrupt flag, but does not clear the flag
  2. Do not interrupt a blocked thread. Otherwise, the thread will throw an exception. You are advised to check the thread status firstgetState()

Thread guard

In Java, the concept of daemon threads, also known as background threads, exists in specific application scenarios

  • When the normal thread ends, the daemon thread stops running
  • The addition of a daemon thread must be placed before the thread starts, otherwise an exception is thrownIllegalThreadStateException
public static void main(String[] args) throws InterruptedException {
    ThreadA threadA = new ThreadA();
    ThreadB threadB = new ThreadB();
    threadA.setName("Thread A");
    threadB.setName("Thread B");
    threadB.setDaemon(true);
    threadA.start();
    threadB.start();
    int i = 20;
    while (i >= 0) {
        i--;
        Thread.sleep(100);
        System.out.print("Status of daemon thread:" + threadB.getState() + "State of user thread:"+ threadA.getState()); System.out.println(); }}static class ThreadA extends Thread {
    @Override
    public void run(a) {
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(The current thread is:+ Thread.currentThread().getName()); }}}static class ThreadB extends Thread {
    @Override
    public void run(a) {
        for (int i = 0; i < 15; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(The current thread is:+ Thread.currentThread().getName()); }}}Copy the code

For daemon threads

  • When the user thread terminates, the daemon thread is also forced to terminate
  • When the user thread has not finished, the daemon thread can finish first

For daemon threads, the JVM virtual machine doesn’t care; When no user thread is running, the VM exits

Thread safety

Now, let’s get to the top priority of thread use, thread concurrency safety!

Imagine if multiple threads were working on a shared piece of data simultaneously. Two threads do increment operations on a static variable at the same time, each increment by 100,000, the final result will be 200,000?

public static Integer C = 0;

public static void main(String[] args) throws InterruptedException {
    new B().start();
    new B().start();
    Thread.sleep(2000);
    System.out.println(C);
}

public static class B extends Thread {
    @Override
    public void run(a) {
        for (int i = 0; i < 1000 _00; i++) { C++; }}}Copy the code

In the example above, two threads were started, adding a total of 200,000 times to the class attribute C. However, the final result is not the desired answer, and the results of each run are inconsistent

This is a thread safety problem when threads are concurrent. There are two main reasons for thread safety problems

  1. Race condition: Multiple threads accessing a shared data simultaneously
  2. Memory visibility: The modified data is not written back to the memory in time

A race condition, well understood, is when multiple threads are working on a piece of data simultaneously. The data is 1, A and B take away at the same time, each add 1, the data is still two, this is the race

The invisibility of memory is more complicated. Thread A modified the shared data, and thread B subsequently acquired the shared data, but not the modified data!

Here is a snippet from Java Programming Logic that demonstrates memory visibility

private static boolean shutdown = false;

static class HelloThread extends Thread {
    @Override
    public void run(a) {
        while(! shutdown) {// do nothing
        }
        System.out.println("exit hello"); }}public static void main(String[] args) throws InterruptedException {
    new HelloThread().start();
    Thread.sleep(1000);
    shutdown = true;
    System.out.println("exit main");
}
Copy the code

To be clear, data modified by a thread may not be written directly back into memory. It stays in the registers, the cache of the CPU, and most computers are multi-core cpus

There are also two cases of thread-safety caused by memory visibility. The modified data cannot be written into memory before the next acquisition. The modified data is directly cached in the CURRENT CPU and is not written to the memory

Race conditions, memory visibility, two factors that lead to thread insecurity