I. Introduction to threads

What is a process

In short, a process can be viewed as a running program. It is the basic unit in which the system runs programs, and therefore the process is dynamic. A process is a running activity of a program with some independent function on a set of data. A process is the basic unit of resource allocation in an operating system.

What is a thread

Thread is the basic unit of scheduling in an operating system. Threads, also known as light-weight processes, can create multiple threads within a Process, each with its own attributes such as counters, stacks, and local variables, and have access to shared memory variables.

The difference between processes and threads

  • A program has at least one process, and a process has at least one thread.
  • Threads are more finely partitioned than processes, so execution overhead is lower and concurrency is higher.
  • A process is an entity with independent resources; Multiple threads in the same process share the resources of the process.

Second, the basic usage of threads

List of basic methods for threads:

methods describe
run The execution entity of a thread.
start The starting method of a thread.
currentThread Returns a reference to the thread object currently executing.
setName Set the thread name.
getName Gets the thread name.
setPriority Set the thread priority. Thread priorities in Java range from [1,10], and in general, high-priority threads have priority at run time. Can be achieved bythread.setPriority(Thread.MAX_PRIORITY)The default priority is 5.
getPriority Gets the thread priority.
setDaemon Set the thread to a daemon thread.
isDaemon Determines whether the thread is a daemon thread.
isAlive Checks whether the thread is started.
interrupt Interrupts the running state of another thread.
interrupted Tests whether the current thread has been interrupted. This method clears the interrupted state of a thread. In other words, if you call this method twice in a row, the second call will return false (unless the current thread breaks again after the first call clears the interrupted state and before the second call checks its state).
join A thread can be forced to run while other threads cannot run and must wait for the thread to complete before continuing.
Thread.sleep Static method. Hibernates the currently executing thread.
Thread.yield Static method. Suspends the currently executing thread and allows another thread to execute.

Create a thread

There are three ways to create a thread:

  • inheritanceThread
  • implementationRunnableinterface
  • implementationCallableinterface

Thread class inheritance

Steps for creating a Thread by inheriting the Thread class:

  1. defineThreadClass, and overrides that classrunMethods.runThe method body of a method represents the task to be completed by the thread, so therunA method is called an executor.
  2. createThreadAn instance of a subclass that creates a thread object.
  3. Of the call thread objectstartMethod to start the thread.
public class ThreadDemo {

    public static void main(String[] args) {
        // instantiate the object
        MyThread tA = new MyThread("Thread Thread - A");
        MyThread tB = new MyThread("Thread Thread - B");
        // Call the thread body
        tA.start();
        tB.start();
    }

    static class MyThread extends Thread {

        private int ticket = 5;

        MyThread(String name) {
            super(name);
        }

        @Override
        public void run(a) {
            while (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + "Sold the first." + ticket + "Ticket"); ticket--; }}}}Copy the code

Implement the Runnable interface

Implementing the Runnable interface is better than inheriting the Thread class because:

  • Java does not support multiple inheritance. All classes are allowed to inherit only one parent class, but can implement multiple interfaces. If you inheritThreadClasses cannot inherit from other classes, which is bad for scaling.
  • A class might just want to be executable, inheriting the whole thingThreadClass overhead is too high.

Steps to create a thread by implementing the Runnable interface:

  1. defineRunnableThe implementation class of the interface and overrides that interfacerunMethods. therunThe method body of a method is also the thread execution body of that thread.
  2. createRunnableImplements an instance of the class and acts as an instanceThreadTarget to createThreadObject, whichThreadObject is the true thread object.
  3. Of the call thread objectstartMethod to start the thread.
public class RunnableDemo {

    public static void main(String[] args) {
        // instantiate the object
        Thread tA = new Thread(new MyThread(), "A Runnable thread - A");
        Thread tB = new Thread(new MyThread(), "A Runnable thread - B");
        // Call the thread body
        tA.start();
        tB.start();
    }

    static class MyThread implements Runnable {

        private int ticket = 5;

        @Override
        public void run(a) {
            while (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + "Sold the first." + ticket + "Ticket"); ticket--; }}}}Copy the code

Implement Callable interface

Neither of the two ways to create a Thread, inheriting the Thread class or implementing the Runnable interface, returns a value. Therefore, after the thread completes execution, it cannot get the result of execution. But what if you expect execution results?

To solve this problem, Java 1.5 has provided the Callable interface and the Future interface, which can return the result of a thread’s execution after it has finished.

Steps to create a thread by implementing the Callable interface:

  1. createCallableInterface implementation class, and implementcallMethods. thecallMethod will be the thread body and have a return value.
  2. createCallableImplements an instance of the class, usingFutureTaskClass to packagingCallableObject, whichFutureTaskObject encapsulates thisCallableThe object’scallMethod return value.
  3. useFutureTaskObject as aThreadObject creates and starts a new thread.
  4. callFutureTaskThe object’sgetMethod to get the return value at the end of thread execution.
public class CallableDemo {

    public static void main(String[] args) {
        Callable<Long> callable = new MyThread();
        FutureTask<Long> future = new FutureTask<>(callable);
        new Thread(future, "Callable thread").start();
        try {
            System.out.println("Task Time:" + (future.get() / 1000000) + "毫秒");
        } catch(InterruptedException | ExecutionException e) { e.printStackTrace(); }}static class MyThread implements Callable<Long> {

        private int ticket = 10000;

        @Override
        public Long call(a) {
            long begin = System.nanoTime();
            while (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + "Sold the first." + ticket + "Ticket");
                ticket--;
            }

            long end = System.nanoTime();
            return(end - begin); }}}Copy the code

FAQ

startrunWhat’s the difference
  • runA method is the execution body of a thread.
  • startThe method starts the thread, which the JVM then tells the thread to executerunMethods.
You can call it directlyThreadOf the classrunMethods?
  • You can. But if you call it directlyThreadrunMethods, it will behave just like normal methods.
  • Must be used to execute our code in a new threadThreadstartMethods.

Thread to sleep

useThread.sleepMethod to sleep a thread that is currently executing.

Using Thread.sleep requires passing it an integer value that represents the number of milliseconds the Thread will sleep.

The Thread.sleep method may throw InterruptedException because exceptions cannot be propagated back to main across threads and must be handled locally. Other exceptions thrown by the thread also need to be handled locally.

public class ThreadSleepDemo {

    public static void main(String[] args) {
        new Thread(new MyThread("Thread A".500)).start();
        new Thread(new MyThread("Thread B".1000)).start();
        new Thread(new MyThread(Thread "C".1500)).start();
    }

    static class MyThread implements Runnable {

        /** Thread name */
        private String name;

        /** Sleep time */
        private int time;

        private MyThread(String name, int time) {
            this.name = name;
            this.time = time;
        }

        @Override
        public void run(a) {
            try {
                // Hibernate for the specified time
                Thread.sleep(this.time);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(this.name + "Sleep" + this.time + "Milliseconds."); }}}Copy the code

Thread comity

The invocation of the Thread.yield method declares that the current Thread has completed the most important part of its life cycle and can be switched to another Thread for execution.

This method is only a suggestion to the thread scheduler, and only a suggestion that other threads with the same priority should run.

public class ThreadYieldDemo {

    public static void main(String[] args) {
        MyThread t = new MyThread();
        new Thread(t, "Thread A").start();
        new Thread(t, "Thread B").start();
    }

    static class MyThread implements Runnable {

        @Override
        public void run(a) {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "Run, I =" + i);
                if (i == 2) {
                    System.out.print("Thread courtesy:");
                    Thread.yield();
                }
            }
        }
    }
}
Copy the code

Termination of the thread

The stop method in Thread is defective and deprecated.

Stopping a Thread with Thread.stop causes it to unlock all locked monitors (a natural consequence of unchecked ThreadDeath exceptions propagating through the stack). If any object previously protected by these monitors is in an inconsistent state, the corrupted object will be visible to other threads, potentially resulting in arbitrary behavior. Many uses of thread.stop should be replaced by code that only modifies some variables to indicate that the target Thread should stop running. The target thread should check this variable periodically and return from its run method in an orderly fashion if it indicates to stop running. If the target thread is waiting for a long time (for example, on a condition variable), the interrupt method should be used to break the wait.

While one thread is running, another thread can interrupt its running state directly with the Interrupt method.

public class ThreadInterruptDemo {

    public static void main(String[] args) {
        MyThread mt = new MyThread(); // instantiate the Runnable subclass object
        Thread t = new Thread(mt, "Thread"); // Instantiate the Thread object
        t.start(); // Start the thread
        try {
            Thread.sleep(2000); // Thread sleep for 2 seconds
        } catch (InterruptedException e) {
            System.out.println("3. Dormancy terminated");
        }
        t.interrupt(); // Interrupts thread execution
    }

    static class MyThread implements Runnable {

        @Override
        public void run(a) {
            System.out.println(1. Enter the run() method);
            try {
                Thread.sleep(10000); // Thread sleep for 10 seconds
                System.out.println("2. Sleep has been completed");
            } catch (InterruptedException e) {
                System.out.println("3. Dormancy terminated");
                return; // return to the call point
            }
            System.out.println("4. The run() method ends normally"); }}}Copy the code

If a thread’s run method executes an infinite loop and does not perform an operation such as sleep that throws InterruptedException, then calling the thread’s interrupt method does not cause the thread to terminate prematurely.

Calling interrupt sets the thread’s interrupt flag, and calling the interrupted method returns true. Therefore, you can use the interrupted method in the body of the loop to determine if the thread is interrupted and terminate the thread early.

There are two ways to safely terminate a thread:

  • definevolatileFlag bit, inrunMethod uses flag bits to control thread termination
  • useinterruptMethods andThread.interruptedMethod is used in combination to control thread termination

Example: Use the volatile flag bit to control thread termination

public class ThreadStopDemo2 {

    public static void main(String[] args) throws Exception {
        MyTask task = new MyTask();
        Thread thread = new Thread(task, "MyTask");
        thread.start();
        TimeUnit.MILLISECONDS.sleep(50);
        task.cancel();
    }

    private static class MyTask implements Runnable {

        private volatile boolean flag = true;

        private volatile long count = 0L;

        @Override
        public void run(a) {
            System.out.println(Thread.currentThread().getName() + "Thread start");
            while (flag) {
                System.out.println(count++);
            }
            System.out.println(Thread.currentThread().getName() + "Thread terminated");
        }

        /** * Thread termination is controlled by the volatile flag */
        public void cancel(a) {
            flag = false; }}}Copy the code

Example: Use the interrupt and thread.interrupted methods together to control Thread termination

public class ThreadStopDemo3 {

    public static void main(String[] args) throws Exception {
        MyTask task = new MyTask();
        Thread thread = new Thread(task, "MyTask");
        thread.start();
        TimeUnit.MILLISECONDS.sleep(50);
        thread.interrupt();
    }

    private static class MyTask implements Runnable {

        private volatile long count = 0L;

        @Override
        public void run(a) {
            System.out.println(Thread.currentThread().getName() + "Thread start");
            // Thread termination is controlled by combining thread.interrupted and interrupt
            while(! Thread.interrupted()) { System.out.println(count++); } System.out.println(Thread.currentThread().getName() +"Thread terminated"); }}}Copy the code

Daemon thread

What is a daemon thread?

  • Daemon threads are threads that execute in the background and do not prevent the JVM from terminating. When all non-daemon threads end, the program terminates, killing all daemon threads.
  • As opposed to a Daemon Thread, a User Thread is a non-daemon Thread.

Why do we need a daemon thread?

  • Daemon threads are of low priority and are used to serve other objects and threads in the system. A typical application is the garbage collector.

How do I use daemon threads?

  • You can useisDaemonMethod to determine whether a thread is a daemon thread.
  • You can usesetDaemonMethod to set the thread to a daemon thread.
    • The running user thread cannot be set as a daemon thread, sosetDaemonMust be inthread.startMethod, otherwise thrownllegalThreadStateExceptionThe exception;
    • A child thread created by a daemon thread is still a daemon thread.
    • Do not assume that all applications can be assigned to daemon threads for services such as read and write operations or computational logic.
public class ThreadDaemonDemo {

    public static void main(String[] args) {
        Thread t = new Thread(new MyThread(), "Thread");
        t.setDaemon(true); // This thread runs in the background
        System.out.println("Is thread T a daemon?" + t.isDaemon());
        t.start(); // Start the thread
    }

    static class MyThread implements Runnable {

        @Override
        public void run(a) {
            while (true) {
                System.out.println(Thread.currentThread().getName() + "It's running."); }}}}Copy the code

See also: Summary of daemon threads in Java

FAQ

What is the difference between sleep, yield, and join methods

  • yieldmethods
    • yieldMethods willLet the thread fromRunningState intoRunnablestate.
    • When calling theyieldAfter method, onlyHaving the same or higher priority as the current threadRunnableThe state thread gets a chance to execute.
  • sleepmethods
    • sleepMethods willLet the thread fromRunningState intoWaitingstate.
    • sleepMethod needs to specify the wait time,When the wait time is exceeded, the JVM sends the thread fromWaitingState intoRunnablestate.
    • When calling thesleepMethods after,Can threads of any priority be given the opportunity to execute.
    • sleepMethod does not release “lock flags”, that is, if there aresynchronizedSynchronize blocks, and other threads still cannot access the shared data.
  • join
    • joinMethods willLet the thread fromRunningState intoWaitingstate.
    • When calling thejoinMethods after,The current thread must wait for the calljoinMethod is not allowed to resume execution until the thread of the method terminates.

Why are sleep and yield methods static

The sleep and yield methods of the Thread class handle threads in the Running state.

So it does not make sense to execute these two methods on other threads that are not in the Running state. That’s why these methods are static. They work in the currently executing thread and prevent programmers from making the mistake of thinking they can be called in another non-running thread.

Whether Java threads are strictly executed according to thread priority

Even if the priority of a thread is set, there is no guarantee that the higher-priority thread will execute first.

The reason is that thread priorities depend on the support of the operating system. However, different operating systems support different thread priorities, which cannot be well matched to Java thread priorities.

3. Communication between threads

When multiple threads can work together to solve a problem, threads need to be coordinated if some parts must be completed before others.

wait/notify/notifyAll

  • waitwaitMethod causes the thread to release the lock on the object it holds,Let the thread fromRunningState intoWaitingstateAnd wait fornotify / notifyAllTo wake up. If the lock is not released, no other thread can enter the synchronization method of the object or the synchronization control block, and the execution cannot be donenotifyornotifyAllTo wake up the suspended thread, causing a deadlock.
  • notify– Wake up a beingWaitingThe JVM controls which thread is awakened.
  • notifyAll– Wake up all beingWaitingState threads, which then need to compete for object locks.

Note:

  • wait,notify,notifyAllAre allObjectMethods in a classRather thanThread.
  • Wait, notify, notifyAll can only be used in a synchronized method or synchronized block of code is used, otherwise it will throw IllegalMonitorStateException at runtime.

Why are Wait, notify, and notifyAll not defined in Threads? Why wait, notify, and notifyAll are used with synchronized?

First of all, we need to know a few basic knowledge points:

  • Each Java object has a monitor corresponding to it.
  • Each monitor has an object lock, a wait queue, and a synchronization queue

With these concepts in mind, let’s go back to the first two questions.

Why aren’t these methods defined in Thread?

Since each Object has an Object lock, it is natural for the current Thread to wait on an Object lock based on that Object, rather than using the current Thread. Because the current Thread may wait for locks from multiple threads, it becomes very complicated to operate on a Thread basis.

Why wait, notify, and notifyAll are used with synchronized?

If the wait method of an object is called, the current thread must own the object lock of the object, so the wait method must be called in a synchronized method and a synchronized block.

The producer-consumer pattern is a classic use case for wait, notify, and notifyAll:

public class ThreadWaitNotifyDemo02 {

    private static final int QUEUE_SIZE = 10;
    private static final PriorityQueue<Integer> queue = new PriorityQueue<>(QUEUE_SIZE);

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

    static class Consumer extends Thread {

        Consumer(String name) {
            super(name);
        }

        @Override
        public void run(a) {
            while (true) {
                synchronized (queue) {
                    while (queue.size() == 0) {
                        try {
                            System.out.println("Queue empty, waiting for data");
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            queue.notifyAll();
                        }
                    }
                    queue.poll(); // Remove the first element each time
                    queue.notifyAll();
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "Removes an element from a queue that currently has:" + queue.size() + "The element"); }}}}static class Producer extends Thread {

        Producer(String name) {
            super(name);
        }

        @Override
        public void run(a) {
            while (true) {
                synchronized (queue) {
                    while (queue.size() == QUEUE_SIZE) {
                        try {
                            System.out.println("Line full, waiting for space.");
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            queue.notifyAll();
                        }
                    }
                    queue.offer(1); // Insert one element at a time
                    queue.notifyAll();
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + Insert an element into a queue that currently has: + queue.size() + "The element");
                }
            }
        }
    }
}
Copy the code

join

In threading operations, you can use the join method to force one thread to run, while the other threads cannot run and must wait for the thread to complete before continuing.

public class ThreadJoinDemo {

    public static void main(String[] args) {
        MyThread mt = new MyThread(); // instantiate the Runnable subclass object
        Thread t = new Thread(mt, "mythread"); // Instantiate the Thread object
        t.start(); // Start the thread
        for (int i = 0; i < 50; i++) {
            if (i > 10) {
                try {
                    t.join(); // The thread is forced to run
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("Main thread runs -->"+ i); }}static class MyThread implements Runnable {

        @Override
        public void run(a) {
            for (int i = 0; i < 50; i++) {
                System.out.println(Thread.currentThread().getName() + " 运行,i = " + i); // Get the current thread name}}}}Copy the code

The pipe

The piped I/O stream differs from the normal file I/o stream or network I/o stream in that it is primarily used for data transfer between threads, and the medium of transfer is memory. Piped input/output streams mainly include the following four concrete implementations: PipedOutputStream, PipedInputStream, PipedReader and PipedWriter. The first two are byte oriented and the last two are character oriented.

public class Piped {

    public static void main(String[] args) throws Exception {
        PipedWriter out = new PipedWriter();
        PipedReader in = new PipedReader();
        // Connect the output stream to the input stream, otherwise IOException will be thrown when used
        out.connect(in);
        Thread printThread = new Thread(new Print(in), "PrintThread");
        printThread.start();
        int receive = 0;
        try {
            while((receive = System.in.read()) ! = -1) { out.write(receive); }}finally{ out.close(); }}static class Print implements Runnable {

        private PipedReader in;

        Print(PipedReader in) {
            this.in = in;
        }

        public void run(a) {
            int receive = 0;
            try {
                while((receive = in.read()) ! = -1) {
                    System.out.print((char) receive); }}catch(IOException e) { e.printStackTrace(); }}}}Copy the code

4. Thread status

There are six different Thread states defined in java.lang.thread. State, and a Thread can only be in one of them at a given time.

Here is a description of each state and how the states are related:

  • New – A thread that has not called the start method is in this state. This state means that the created thread has not yet been started.

  • Runnable – The thread that has called the start method is in this state. This state means that the thread is already running in the JVM. At the operating system level, however, it may be running, or it may wait for resource scheduling (for example, processor resources) to complete and enter the running state. Therefore, the runnable state means that it can be run. Whether it can be run depends on the resource scheduling of the underlying operating system.

  • Blocked – A monitor lock is requested to enter a synchronized function or block, but the monitor lock is already occupied by another thread and is therefore Blocked. To end this state into Runnable, another thread is required to release the Monitor Lock. This state means that the thread is blocked.

  • Waiting – This state means that a thread is Waiting to be woken up explicitly by another thread. The difference between blocking and waiting is that blocking is passive, waiting for a Monitor Lock to be acquired. Wait is active and is entered by calling methods such as Object.wait.

    Enter the method Exit the method
    No Timeout parameter is setObject.waitmethods Object.notify / Object.notifyAll
    No Timeout parameter is setThread.joinmethods The called thread completes execution
    LockSupport.parkmethods LockSupport.unpark
  • Timed waiting – This state means that the system wakes up automatically after a certain amount of time without waiting explicitly for another thread to wake up.

    Enter the method Exit the method
    Thread.sleepmethods End of the time
    The Timeout parameter is setObject.waitmethods End of time /Object.notify / Object.notifyAll
    The Timeout parameter is setThread.joinmethods Time ends/called thread completes execution
    LockSupport.parkNanosmethods LockSupport.unpark
    LockSupport.parkUntilmethods LockSupport.unpark
  • Terminated (Terminated) – The thread run method ends execution or exits the run method due to an exception. This state means that the thread has ended its life cycle.

The resources

  • Java Concurrent Programming
  • The Art of Concurrent Programming in Java
  • Process and thread relationships and differences
  • The difference between yield and Join methods in Java threads
  • The differences between sleep(), wait(), yield() and Join () methods
  • Java concurrent programming: Two ways to collaborate between threads: Wait, notify, notifyAll, and Condition
  • Java concurrent programming: Callable, Future, and FutureTask
  • StackOverflow VisualVM – Thread States
  • Summary of daemon threads in Java
  • Java concurrency
  • Why must wait() always be in synchronized block