Concepts related to Thread

The thread is the smallest unit of system resource allocation. It is contained in the process and is the actual operating unit of the process. The JVM allows an application to run at the same time, executing multiple threads, each of which has priority, with higher priority threads taking precedence over lower priority threads

There are two types of threads in Java: User threads and daemons.

When the JVM starts, main is called. The Thread on which main resides is a user Thread. New threads created in this Thread are user threads by default, but thread.setdaemon (true) can be set to daemon threads (called before thread.start ()). Daemon threads are the nanny of all non-daemon threads in the JVM. The most typical use of daemon threads is GC (garbage collector).

Daemons work as long as there are any non-daemons in the current JVM instance that have not terminated, and when the last user thread in the JVM terminates, the daemon thread terminates with the JVM


Thread creation mode

Threads can be created in one of two ways: by inheriting the Thread class or implementing the Runnable interface

The Thread class itself is created by implementing the Runnable interface. When creating a thread, you can give it a name that can be repeated. If you do not specify a name when you create a thread, a new name is generated for it

Thread class creation and start:

     class PrimeThread extends Thread {
         long minPrime;
         PrimeThread(long minPrime) {
             this.minPrime = minPrime;
         }

         public void run() {
             // compute primes larger than minPrime
         }
     }
Copy the code
     PrimeThread p = new PrimeThread(143);
     p.start();
Copy the code

Implement Runnable interface creation and startup:

     class PrimeRun implements Runnable {
         long minPrime;
         PrimeRun(long minPrime) {
             this.minPrime = minPrime;
         }

         public void run() {
             // compute primes larger than minPrime
         }
     }
Copy the code
     PrimeRun p = new PrimeRun(143);
     new Thread(p).start();
Copy the code

Thread Status change

The state of a Thread is TERMINATED in the Thread class. It is NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, and TERMINATED.

Public enum State {NEW, // not yet started RUNNABLE, // RUNNABLE, but it may still be WAITING for the CPU resource to be allocated. // BLOCKED, // WAITING, Wait for another thread to finish TIMED_WAITING, // timed wait state TERMINATED; // The thread has finished executing}Copy the code


Key methods in the Thread class source code

Thread.start() : Starts the Thread

When you call start(), it checks to see if the thread has been started for the first time, i.e. ThreadStatus == 0. = 0 current startup for repeat, this is not allowed, IllegalThreadStateException exception will be thrown the thread state error

After verification, the instance object of the current thread is added to the thread group, and the local method start0() is used to start the thread. Remove resources from threadStartFailed() after startup

    public synchronized void start() {

        if(threadStatus ! = 0) throw new IllegalThreadStateException(); group.add(this); boolean started =false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if(! started) { group.threadStartFailed(this); }} catch (Throwable ignore) {···}} private native void start0();Copy the code

Thread.stop() : forces Thread execution to stop

This method is not safe and may have unexpected results, such as shutting down the power supply instead of the normal shutdown operation. The result is the same but the process is completely different

Calling stop() will raise a ThreadDeath exception, at which point the run method will terminate and the thread will be terminated by throwing an exception

When thread.stop() is called, all locks held by the child thread are released. Generally, any block of code that locks the child thread is used to protect data consistency. If all locks held by the thread are suddenly released (uncontrollable) after calling thread.stop(), the protected data may appear inconsistent. Other threads using this corrupted data can cause some very strange application errors

    @Deprecated
    public final void stop() {
        SecurityManager security = System.getSecurityManager();
        if(security ! = null) { checkAccess();if (this != Thread.currentThread()) {
                security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
            }
        }

        if(threadStatus ! = 0) { resume(); // Wake up threadif it was suspended; no-op otherwise
        }

        stop0(new ThreadDeath());
    }

    @Deprecated
    public final synchronized void stop(Throwable obj) {
        throw new UnsupportedOperationException();
    }
Copy the code

Thread.run() : The block of code that the Thread actually runs

Note that if you want to start a thread, calling the run() method directly does not work, and it does not produce any real results

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

Thread.interrupt() : Modifies the interrupt state to reasonably and safely interrupt the current Thread

The stop() method can also interrupt a thread, but it terminates immediately, causing unknown problems, hence the interrupt() method, which can be used to conditionally terminate a thread and keep data safe

To truly interrupt a thread, interrupt() needs to be used in conjunction with isInterrupted() or interrupted(), which get whether or not the interrupt flag is true, and then do what is appropriate. Calling isInterrupted() returns to the interrupted state but does not restore it. After interrupted() returns to the interrupted state and clears the interrupted state

    public void interrupt() {
        if(this ! = Thread.currentThread()) checkAccess(); synchronized (blockerLock) { Interruptible b = blocker;if(b ! = null) { interrupt0(); // Just toset the interrupt flag
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }
Copy the code
    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }
Copy the code
    public boolean isInterrupted() {
        return isInterrupted(false);
    }
Copy the code

Thread.yield() : Pauses execution of the current Thread to give the CPU time to execute other threads (but may be ignored)

In fact, if you want yield() to do its job, you need to use it with thread priority. The actual running process of yield() is to check if any threads of the same priority are currently in the same runnable state, and if so, to give the CPU to that thread, otherwise the original thread will continue to run. So the yield() method is called “yield,” and it cedes the opportunity to run to another thread of equal priority.

    public static native void yield();
Copy the code

Thread.wait()/thread.wait (long) : Causes the Thread calling this method to release the shared resource lock, exit from the running state, and wait until it is woken up again or timed to wait N milliseconds, and returns if no notification is given

Wait () belongs to Object. To use a lock, you first need to acquire it, which is usually synchronized or synchronized and woken up by notify() or notifyAll()

    public final void wait() throws InterruptedException {
        wait(0);
    }

    public final native void wait(long timeout) throws InterruptedException;
Copy the code

Thread.join()/thread.join (long) : wait for the end of the Thread to continue

The thread that is currently executing is blocked until the thread that called the join method finishes executing or is interrupted, which is mainly used for the interaction between threads

public final void join() throws InterruptedException { join(0); } public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis();  long now = 0;if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0); }}else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay); now = System.currentTimeMillis() - base; }}}Copy the code

Thread.sleep(long) : Causes the current Thread to suspend execution for a specified period of time

Calling interrupt() on this thread object during a pause wakes the thread and throws InterruptedException before execution resumes

    public static void sleep(long millis, int nanos)
    throws InterruptedException {
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if(nanos >= 500000 || (nanos ! = 0 && millis == 0)) { millis++; } sleep(millis); }Copy the code

Thread.notify()/thread.notifyall () : wakes up the waiting Thread

A common scenario is that thread A invokes wait() of object B to enter the waiting state, while another thread C invokes notify()/notifyAll() of object B. After receiving the notification, thread A exits the waiting queue and enters the runnable state to perform subsequent operations. Threads A and C interact with each other through object B, and the relationship between wait() and notify()/notifyAll() on the object is like A switch signal to complete the interaction between the wait and notify.

Notify () randomly wakes up a thread in the wait queue waiting for the same shared resource. This thread exits the wait queue and enters the runnable state

NotifyAll () wakes up all threads in the waiting queue waiting for the same shared resource. All threads exit the waiting queue and enter the runnable state. NotifyAll with the highest priority starts execution

   public final native void notify();

   public final native void notifyAll();
Copy the code

Other methods in the Thread class source code

CurrentThread () : Returns a reference to the thread object currently executing

    /**
     * Returns a reference to the currently executing thread object.
     *
     * @return  the currently executing thread.
     */
    public static native Thread currentThread();
Copy the code

This is a Native method, this method is implemented in C/C++ language, and compiled into a DLL, by Java to call, function implementation body in DLL, JDK source code does not contain, so we can not see. This is also the underlying mechanism of Java. In fact, Java calls different native methods on different platforms to achieve access to the operating system.

    System.out.println("Object information:"+ Thread.currentThread()); //// Object information: Thread[main,5,main]Copy the code

GetId () : returns the ID of the thread

    /**
     * Returns the identifier of this Thread.  The thread ID is a positive
     * <tt>long</tt> number generated when this thread was created.
     * The thread ID is unique and remains unchanged during its lifetime.
     * When a thread is terminated, this thread ID may be reused.
     *
     * @return this thread*/ public long getId() {return getId; } // set tid = nextThreadID(); ··· · // Tid equals threadSeqNumber, Private static synchronized long nextThreadID() {return ++threadSeqNumber; }Copy the code

The thread ID is of type long, generated by the nextThreadID method. The nextThreadID method is thread-safe (synchronized modification), and each new thread ID is ++ and assigned to tid

    System.out.println("Thread ID:"+ Thread.currentThread().getId()); //// Output (called in the main method) Thread ID: 1Copy the code

**getName() : ** Gets the name of the thread

    /**
     * Returns this thread's name. * * @return this thread's name.
     * @see     #setName(String)
     */
    public final String getName() {
        return name;
    }
Copy the code

The Thread name is of type String and the default is thread-n (N: the order in which threads are created, starting from 0). Of course, the Thread class also provides two ways to change the name of a Thread: new Thread(“name”) or thread.setName (“name”).

	Thread threadTest01 = new Thread();
	System.out.println("Thread name:" + threadTest01.getName());
	
	Thread threadTest02 = new Thread();
	System.out.println("Thread name:" + threadTest02.getName());
	
	Thread threadTest03 = new Thread("I have a name. My name is T03.");
	System.out.println("Thread name:" + threadTest03.getName());
	
	Thread threadTest04 = new Thread();
	threadTest04.setName("I have a name: MY name is T04");
	System.out.println("Thread name:"+ threadTest04.getName()); //// Output Result Thread name: thread-0 Thread name: thread-1 Thread name: I have a name and my name is T03 Thread name: I have a name and my name is T04Copy the code

GetPriority () : Gets the thread priority

/** * The minimum priority that a thread can have. */ public final static int MIN_PRIORITY = 1; /** * The default priority that is assigned to a thread. */ public final static int NORM_PRIORITY = 5; /** * The maximum priority that a thread can have. */ public final static int MAX_PRIORITY = 10; // Thread priority is set during init, Private void init(ThreadGroup G, Runnable Target, String Name, Long stackSize, AccessControlContext acc, boolean inheritThreadLocals) { ... Thread parent = currentThread(); ... this. Priority = parent. GetPriority (); ···} /** * Returns this thread's priority. * * @return this thread's priority.
     * @see     #setPriority
     */
    public final int getPriority() {
        return priority;
    }
Copy the code

The default thread priority is the parent thread priority: this.priority = parent.getPriority(); . The priority of a thread does not determine the execution order of the thread, but a higher priority is more likely to obtain CPU resources

The priority of a thread can be set using setPriority(int newPriority). The value ranges from 1 to 10. The default value is 5

    public final void setPriority(int newPriority) {
        ThreadGroup g;
        checkAccess();
        if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
            throw new IllegalArgumentException();
        }
        if((g = getThreadGroup()) ! = null) {if (newPriority > g.getMaxPriority()) {
                newPriority = g.getMaxPriority();
            }
            setPriority0(priority = newPriority); }}Copy the code

Throw New IllegalArgumentException(); throw new IllegalArgumentException(); NewPriority = g.goetMaxPriority (); newPriority = g.goetMaxPriority (); newPriority = g.goetMaxPriority (); That is as follows:

public static void main(String[] args) { ThreadMethods main = new ThreadMethods(); Thread t01 = main.new MyThread01(); Thread t02 = main.new MyThread02(); t01.setPriority(Thread.MAX_PRIORITY); t02.setPriority(Thread.MIN_PRIORITY); t02.start(); t01.start(); } /** * @des test Thread01 ** / class MyThread01 extends Thread{public voidrun() {
		super.run();
		
		for(int i = 0 ; i < 10 ; i++ ){
			System.out.println("MyThread01:"+ i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }}}} /** * @des Test Thread02 ** / class MyThread02 extends Thread{public voidrun() {
		super.run();
		
		for(int i = 0 ; i < 10 ; i++ ){
			System.out.println("MyThread02:"+ i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }}}} //// The output is mythread01:0 mythread02:0 mythread02:1 Mythread01:1 mythread02:2 mythread01:2 mythread02:3 MyThread01:3 MyThread01:4 MyThread02:4 MyThread01:5 MyThread02:5 MyThread01:6 MyThread02:6 MyThread01:7 MyThread02:7 MyThread01:8 MyThread02:8 MyThread01:9 MyThread02:9Copy the code

Common problems with Thread usage

Deadlock causes and how to avoid and solve

A deadlock occurs when multiple threads are blocked at the same time, and one or all of them are waiting for a resource to be released. Since the thread is blocked indefinitely, the other threads cannot wait for the resource to be released, so the program cannot continue to run normally. A chest containing priceless treasure requires two keys to open. Two people each have a key, but each of them is waiting for the other to hand over his key first, but neither of them will hand over theirs and the standoff persists.)

Why do deadlocks occur?

The following four conditions must be met for a deadlock to occur. If any of these conditions is not true, the deadlock will not occur

  • Mutual exclusion: Threads require exclusive control over allocated resources, that is, a resource is owned by only one process for a period of time. If another process requests the resource. The requesting process can only wait.
  • Non-expropriation condition: a resource acquired by a process cannot be forcibly seized by another process before it is fully used, that is, it can only be released by the thread that acquired the resource itself (only voluntarily).
  • Request and hold conditions: a thread that has held at least one resource, but makes a request for a new resource that has been occupied by another thread, is blocked but holds on to its acquired resource.
  • Circular waiting condition: There is a circular waiting chain of thread resources in which the resource acquired by each thread is also requested by the next thread in the chain.

Deadlock condition summary:

  1. At least one resource cannot be shared. Procedure
  2. At least one task must hold a resource and wait to acquire another resource held by another task
  3. Resources cannot be preempted
  4. There must be a loop waiting

How to avoid a settlement deadlock?

1. Avoid nested locks This is the most common cause of deadlocks. If you already hold one resource, avoid locking another. Deadlock situations are almost impossible if only one object lock is used. For example, the following code changes the nesting part of the loop above to avoid deadlock situations:

public void run() {
        String name = Thread.currentThread().getName();
        System.out.println(name + " acquiring lock on " + obj1);
        synchronized (obj1) {
            System.out.println(name + " acquired lock on " + obj1);
            work();
        }
        System.out.println(name + " released lock on " + obj1);
        System.out.println(name + " acquiring lock on " + obj2);
        synchronized (obj2) {
            System.out.println(name + " acquired lock on " + obj2);
            work();
        }
        System.out.println(name + " released lock on " + obj2);

        System.out.println(name + " finished execution.");
    }
Copy the code

For example, in the above program, we lock the entire object resource, but if we only need one of the fields, we should only lock that particular field and not the entire object

If two threads wait indefinitely for each other using Thread Join, deadlock can also occur. We can set the maximum waiting time to avoid this situation.

Types and differences of locks in JAVA

In the process of code execution, some data need to be controlled exclusively to ensure the correctness of the final calculation result, so there needs to be a mechanism to ensure that the data is locked in the process of execution and will not be modified by the outside, and this mechanism is the locking mechanism

At the same time, according to the characteristics of the lock, design, state, and can be loosely divided into the following categories:

Fair lock/unfair lock

A fair lock is one in which multiple threads acquire locks in the order in which they are applied. An unfair lock is one in which the order is not completely random and may cause priority reversal or starvation

Synchronized is an unfair lock, ReentrantLock uses the constructor to determine whether the lock is fair or unfair, and mode is an unfair lock

The throughput performance of unfair locks is greater than that of fair locks

Reentrant lock

Also called a recursive lock, it means that the inner method automatically acquires the lock when the outer method acquires the lock

Synchronized and ReentranLock are reentrant locks that prevent deadlocks to some extent

Exclusive lock/shared lock

An exclusive lock means that the lock can only be held by one county at a time. A shared lock means that the lock can be held by more than one county

Synchronized and ReentranLock are exclusive locks. The read lock of ReadWriteLock is a shared lock and the write lock is an exclusive lock. ReentrantLock’s exclusive and shared locks are also implemented through AQS

Mutex/read-write lock

Mutex = exclusive lock, read/write lock = shared lock. A mutex is a ReentrantLock, and a read-write lock is a ReadWriteLock

Optimistic lock/pessimistic lock

They don’t fall into a specific lock category, but rather a concurrent synchronization perspective

Optimistic locks believe that concurrent operations on the same data will not be modified, and the data will be updated continuously. Optimistic locks believe that concurrent operations without locking will be fine

Pessimistic lock believes that concurrent operations on the same data must be modified, so for concurrent operations on unified data, pessimistic lock takes the form of locking, because pessimistic lock believes that operations without locking must have problems

Pessimistic locks are suitable for scenarios with a large number of operations, while optimistic locks are suitable for scenarios with a large number of reads and writes. No lock can greatly improve performance

Segmented lock

It’s a locking strategy, not a specific lock. For example, ConcurrentHashMap concurrency is implemented in the form of segmented locking to achieve efficient concurrent operations

When you want to put an element, you don’t lock the entire hashMap. Instead, you use hashCode to know which segment it’s going to be placed on, and then lock the segment. So multithreading put elements on different segments is really parallel insertion. However, we need to get all the segment locks to calculate the size

Segmental locks are designed to refine the granularity of locks

Bias lock/lightweight lock/heavyweight lock

This is generalized by lock state and is specific to Synchronized. In order to reduce the performance problems caused by lock acquisition and lock release, Java 1.6 introduced a state that will gradually upgrade with the competition situation. Locks can be upgraded but cannot be degraded, meaning that biased locks cannot be retracted after upgrading to lightweight locks. This upgrade cannot be degraded policy is aimed at improving the efficiency of live locks and lock release

spinlocks

In fact, compared with the concept of mutex, the mutex thread will enter the magic of WAITING state and RUNNABLE state, which involves the overhead of context switch and CPU preemption. The thread of spin lock will always be RUNNABLE state, and it will constantly check the lock flag there. However, the whole process of spread-locking consumes CPU. Although the initial cost is lower than that of mutex, the cost of spread-locking increases linearly with the holding time

Interruptible lock

Synchronized is uninterruptible and Lock is interruptible

Interruptible here is built on blocking wait interrupts, which cannot be interrupted during operation


Synchronized Mechanism of synchronization lock

This article will continue to update the thread related knowledge, together to check the missing learn fun!