Three ways to create a thread

1. Inherit the Thread class

public class ThreadTest01 extends Thread {

    @Override
    public void run(a) {
        for (int i = 0; i < 10; i++) {
            System.out.println("run--->"+ i); }}public static void main(String[] args) {
        Thread thread = new ThreadTest01();
        thread.start();

        for (int i = 0; i < 10; i++) {
            System.out.println("main====>"+ i); }}}Copy the code

Not recommended to avoid the limitations of OOP single inheritance

2. Implement the Runnable interface

public class ThreadTest02 implements Runnable {
    @Override
    public void run(a) {
        for (int i = 0; i < 10; i++) {
            System.out.println("run--->"+ i); }}public static void main(String[] args) {
        ThreadTest02 t = new ThreadTest02();
        Thread thread = new Thread(t);
        thread.start();

        for (int i = 0; i < 10; i++) {
            System.out.println("main====>"+ i); }}}Copy the code

Recommended use: Avoid the limitation of single inheritance, flexible and convenient, convenient to use the same object by multiple threads

Simplify code with anonymous inner classes:

interface Test {
    void fun(a);
}

public class AnonymousClassesTest {
    public static void main(String[] args) {
		// Anonymous inner class
		Test test = new Test() {
            public void fun(a) {
                System.out.println("Anonymous inner class"); }}; test.fun();// Output: anonymous inner class

		// Create threads using anonymous inner classes
		new Thread(new Runnable() {
            @Override
            public void run(a) {
                System.out.println("abc");
            }
        }).start();  // abc
}
Copy the code

Use lambda expressions to simplify code: any interface that contains only one abstract method is a functional interface. For functional interfaces, we can create objects for that interface through lambda expressions.

interface Test {
    void fun(a);
}

public class LambdaTest {
    public static void main(String[] args) {
    	// lambda expressions
    	// The contents of the braces are equivalent to overriding the methods in the interface. If the code is only one line, you can remove the braces
        test = () -> {
            System.out.println("i like lambda");
        };

        test.fun();  // Output: I like lambda
        
        // Use lambda expressions to create threads
		new Thread( () -> System.out.println("abc") ).start();  // abc}}Copy the code

3. Implement the Callable interface

public class ThreadTest04 implements Callable<String> {
    @Override
    public String call(a) throws Exception {

        return "Return value";
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<String> c1 = new ThreadTest04();
        Callable<String> c2 = new ThreadTest04();
        Callable<String> c3 = new ThreadTest04();

        // Create the execution service
        ExecutorService ser = Executors.newFixedThreadPool(3);

        // Commit execution
        Future<String> f1 = ser.submit(c1);
        Future<String> f2 = ser.submit(c2);
        Future<String> f3 = ser.submit(c3);

        // Get the result
        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());

        // Shut down the serviceser.shutdown(); }}Copy the code

Priority of the thread

In Java threads, priority is controlled by an integer member variable priority, which ranges from 1 to 10. The default priority is 5. The priority can be set by setPriority(int). Threads with a higher priority allocate more time slices than threads with a lower priority. However, thread priority cannot be used as a dependency on program correctness.

Six states of Java threads

  • NEW: Initial state, the thread is built, but the start() method has not been called
  • Java threads refer to the ready and running states of the operating system as RUNNABLE.
  • BLOCKED: the thread is BLOCKED in a lock
  • WAITING: a state in which the current thread is WAITING for some specific action (notification or interrupt) from another thread
  • TIME_WAITING: a timeout WAITING state that, unlike WAITING, can return at a specified time
  • TERMINATED: indicates that the current thread is TERMINATED

The blocking state is the state in which a thread blocks when it enters a method or code block modified by the synchronized keyword (obtaining the Lock), whereas threads blocking the Lock interface in the Java.concurrent package are in the wait state. Because the Lock interface in the Java.Concurrent package uses the related methods in the LockSupport class for blocking implementations.

Daemon thread

A Daemon is a support thread because it is used for background scheduling and support work in applications. This means that a Java virtual machine will exit when there are no non-Daemon threads in the machine. So when building Daemon threads, you can’t rely on the contents of the finally block to ensure that the logic to close or clean up resources is performed, because the contents of the finally block are not necessarily executed. The thread can be set to a Daemon thread by calling setDaemonn(true) (the default is false).

public class TestDaemon {
    public static void main(String[] args) {
        Thread thread = new Thread(new MyDaemon(), "Daemon thread");
        thread.setDaemon(true); thread.start(); }}class MyDaemon implements Runnable {

    @Override
    public void run(a) {
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("I'm the daemon thread"); }}}Copy the code

Executing the above code will find no print.

interrupt

A thread terminates automatically when it finishes executing, or prematurely if an exception occurs during execution. InterruptedException is thrown to terminate a thread prematurely by calling interrupt() on the thread if it is blocked, waiting, or timed out. However, I/O and synchronized lock blocking cannot be interrupted. A thread checks for interruption by using isInterrupted(). If the thread has been interrupted, this method returns false even if it has been interrupted.

public class Interrupted {
    public static void main(String[] args) throws InterruptedException {
        // sleepThread keeps trying to sleep
        Thread sleepThread = new Thread(new SleepRunner(), "sleepThread");
        // BusyThread keeps running
        Thread busyThread = new Thread(new BusyRunner(), "busyThread");
        sleepThread.setDaemon(true);
        busyThread.setDaemon(true);
        sleepThread.start();
        busyThread.start();

        // Sleep for 5 seconds to allow sleepThreads and busyThreads to run fully
        Thread.sleep(5000);
        sleepThread.interrupt();
        busyThread.interrupt();

        System.out.println("sleepThread interrupted is " + sleepThread.isInterrupted());  // false
        System.out.println("busyThread interrupted is " + busyThread.isInterrupted());    // true

        // Prevent sleepThreads and busythreads from exiting immediately
        Thread.sleep(2000); }}class SleepRunner implements Runnable {

    @Override
    public void run(a) {
        while (true) {
            try {
                Thread.sleep(10000);
            } catch(InterruptedException e) { e.printStackTrace(); }}}}class BusyRunner implements Runnable {

    @Override
    public void run(a) {
        while (true) {}}}Copy the code

Results: sleepThread interrupted is false busyThread interrupted is true Java. Lang. InterruptedException: sleep interrupted

If a sleepThread is in the timeout waiting state and is interrupted, an exception will be thrown and the thread will be terminated prematurely. In this case, it checks whether the thread is interrupted and returns false.

Wait/notification mechanism

One thread modifies the value of an object, and another thread senses the change and acts accordingly, starting in one thread and ending in another. The former is the producer, while the latter is the consumer. This pattern separates the “what” and “how”, achieves decoupling at the functional level and scales well architecturally. Java implements similar functionality through a wait/notification mechanism. The wait/notification related methods are available to any Java Object, because they are defined on the superclass java.lang.object of all objects, with the following methods:

  • Notify () : Notifies a thread waiting on an object to return from wait() if the thread has acquired the lock on the object
  • NotifyAll () : Notifies all threads waiting on the object
  • Wait () : The thread that called wait() is in a WAITING state. The thread that called wait() is WAITING for notification from another thread or is interrupted. Note that after calling wait(), the object lock is released.
  • Wait (long) : wait for more than a certain amount of time (n milliseconds) and return if no notification is given.
  • Wait (long, int) : For more granular control of the timeout, up to nanoseconds.

The wait/notification mechanism refers to that thread A calls the wait() method of object O to enter the wait state, and another thread B calls the notify() or notifyAll() method of object O. After receiving the notification, thread A returns from the wait() method of object O and performs subsequent operations.

import java.text.SimpleDateFormat;
import java.util.Date;

public class WaitNotify {
    static Object lock = new Object();
    static boolean flag = true;

    public static void main(String[] args) throws InterruptedException {
        Thread waitThread = new Thread(new Wait(), "waitThread");
        waitThread.start();
        Thread.sleep(1000);
        Thread notifyThread = new Thread(new Notify(), "notifyThread");
        notifyThread.start();
    }

    static class Wait implements Runnable {
        @Override
        public void run(a) {
            synchronized (lock) {
                // If the condition is not met, continue to wait and release the lock
                while (flag) {
                    try {
                        System.out.println(Thread.currentThread().getName() + " flag is true wait @ " +
                                new SimpleDateFormat("HH:mm:ss").format(new Date()));
                        lock.wait();
                    } catch(InterruptedException e) { e.printStackTrace(); }}// If the conditions are met, the work is completed
                System.out.println(Thread.currentThread().getName() + "flag is false running @ " +
                        new SimpleDateFormat("HH:mm:ss").format(newDate())); }}}static class Notify implements Runnable {
        @Override
        public void run(a) {
            synchronized (lock) {
                // Obtain the lock of the lock, and notify. The notification will not release the lock
                System.out.println(Thread.currentThread().getName() + "hold lock notify @ " +
                        new SimpleDateFormat("HH:mm:ss").format(new Date()));
                lock.notifyAll();
                flag = false;
                try {
                    Thread.sleep(2000);
                } catch(InterruptedException e) { e.printStackTrace(); }}// Lock again
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + "hold lock again @ " +
                        new SimpleDateFormat("HH:mm:ss").format(new Date()));
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
Copy the code
Execution Result:  waitThread flag is true wait @ 17:33:39 notifyThreadhold lock notify @ 17:33:40 notifyThreadhold lock again @ 17:33:42 waitThreadflag is false running @ 17:33:44Copy the code

The output order of the third and fourth lines might be reversed.

The above examples illustrate the details of calling wait(), notify(), and notifyAll() :

  • Using wait(), notify(), and notifyAll() requires locking the calling object first.
  • After calling wait(), the thread state changes from RUNNING to WAITING and the current thread is placed on the object’s wait queue.
  • After notify() or notifyAll() is called, the waiting thread does not release the lock from wait() until the thread that calls notify() or notifyAll() releases the lock.
  • The notify() method moves one thread in the wait queue from the wait queue to the synchronization queue, and notifyAll() method moves all threads in the wait queue to the synchronization queue, and the status of the moved thread changes from WAITING to BLOCKED.
  • The return from wait() is conditional upon obtaining the lock of the calling object.

Wait and notify

The classic paradigm of wait/notification

The waiting party (consumer) shall abide by the following principles:

  • Gets the lock of the object
  • If the condition is not met, the object’s wait() method is called and the condition is still checked after being notified
  • If the conditions are met, the corresponding logic is executed
Synchronized (object) {while(condition not satisfied) {object.wait (); } Corresponding processing logic}Copy the code

The notifying party (producer) shall observe the following principles:

  • Get the lock of the object
  • Change the conditions
  • Notifies all threads waiting on an object
Synchronized (object) {change the condition object. NotifyAll ();Copy the code

Pipeline input/output streams

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 transfer medium is memory. There are four specific implementations of piped input/output streams: PipedInputStream, PipedOutputStream, 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 IOException {
        PipedReader reader = new PipedReader();
        PipedWriter writer = new PipedWriter();

        // Connect the input and output streams, otherwise IOException will be thrown when used
        reader.connect(writer);

        Thread printThread = new Thread(new Print(reader), "printThread");
        printThread.start();
        int receive = 0;

        try {
            while((receive = System.in.read()) ! = -1) { writer.write(receive); }}finally{ writer.close(); }}}class Print implements Runnable {
    private PipedReader reader;
    public Print(PipedReader reader) {
        this.reader = reader;
    }

    @Override
    public void run(a) {
        int receive = 0;
        try {
            while((receive = reader.read()) ! = -1) {
                System.out.print((char) receive); }}catch(IOException e) { e.printStackTrace(); }}}Copy the code
Run the program, enter a string, and see the string output as it is: Hello World! Hello World!Copy the code

The use of the Thread. The join ()

If thread A executes thread.join(), it means that thread A is waiting for thread A to terminate before returning from Thread.join (). Thread() provides join() as well as join(long millis) and JOIN (Long millis, int nanos). These two timeout methods indicate that a thread will be returned from the timeout method if it does not terminate within a given timeout period.

public class TestJoin {
    public static void main(String[] args) {
        Thread one = new Thread(new JoinOne(), "One thread");
        Thread two = new Thread(new JoinTwo(one), "Two" thread); one.start(); two.start(); }}class JoinOne implements Runnable {

    @Override
    public void run(a) {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "- >"+ i); }}}class JoinTwo implements Runnable {
    private Thread thread;
    public JoinTwo(Thread thread) {
        this.thread = thread;
    }

    @Override
    public void run(a) {
        try {
            thread.join(0);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "- >"+ i); }}}Copy the code

Executing the code above shows that the printing of thread two begins after thread one finishes.

In the source code of Thread.join(), the join() method calls the overloaded join(long millis), passing in 0, so thread.join () and Thread.join(0) are the same. The underlying join method calls the wait() method

Public final synchronized void join() throws InterruptedException {// If the condition is not met, continue waiting while (isAlive()) {wait(0); } // If the condition is met, the method returns}Copy the code