Beginner multithreading

Threads and processes

process

  • It refers to an application running in memory, and each process has an independent memory space

thread

  • It is an execution path in a process. It shares a memory space. Threads can switch freely and execute concurrently. A process has at least one thread

  • Threads are actually further divided on the basis of processes. After a process is started, several execution paths in it can be divided into several threads

Thread scheduling

Time sharing scheduling

  • All threads use the right to use the CPU in turn, and allocate the CPU time of each thread equally.

preemptive scheduling

  • Give priority to the threads with high priority to use the CPU. If the threads have the same priority, one will be selected randomly (thread randomness). Java uses preemptive scheduling

  • The CPU uses preemptive scheduling mode to switch between multiple threads at high speed. For a core of the CPU, only one thread can be executed at a certain time, and the switching speed of the CPU between multiple threads is faster than our feeling. It seems that the upper area runs at the same time. In fact, multithreaded programs can not improve the running speed of programs, but can improve the running efficiency of programs and make the CPU utilization higher.

Synchronous and asynchronous

Synchronization: queued execution, inefficient but safe

Asynchronous: simultaneous execution, high efficiency, but data is not safe

Concurrency and parallelism

Concurrency: two or more events occur in the same time period

Parallel: two or more events occur at the same time (at the same time)

Thread blocking

One thread takes some time to execute, and another thread also needs to execute at this time. However, before the execution of this thread is completed, the other thread cannot continue to execute, which will cause thread blocking.

All time consuming operations: file reading, receiving user input, etc

Also known as: time consuming operation

Thread interrupt

  • A thread is an independent execution path. Whether it should end or not should be determined by itself

example

public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable());
        t.start();

        for (int i = 10; i < 15; i++) {
            System.out.println(Thread.currentThread().getName() + " : " + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                System.out.println(e.getMessage());
            }
        }
        // Add interrupt flag to thread t
        t.interrupt();
    }

    private static class MyRunnable implements Runnable {

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + " : " + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    System.out.println("Found interrupt flag, thread interrupt");
                    return;
                }
            }
        }
    }

Output:

main : 10
Thread-0 : 0
main : 11
Thread-0 : 1
Thread-0 : 2
main : 12
main : 13
Thread-0 : 3
main : 14
Thread-0 : 4
Thread-0 : 5
 Found interrupt flag, thread interrupt

Thread insecurity

public static void main(String[] args) {
        // Thread unsafe
        Runnable r = new Ticket();
        new Thread(r).start();
        new Thread(r).start();
        new Thread(r).start();
    }

    static class Ticket implements Runnable {
        // Number of votes
        private int count = 10;
        @Override
        public void run() {
            while (this.count > 0){
                // Selling tickets
                System.out.println("Preparing to sell tickets");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.count--;
                System.out.println("Successful ticket issuance, remaining tickets:" + this.count);
            }
        }
Output:
    Preparing to sell tickets
    Preparing to sell tickets
    Preparing to sell tickets
    Tickets issued successfully, remaining tickets: 7
    Preparing to sell tickets
    Ticket issued successfully, remaining tickets: 9
    Preparing to sell tickets
    Ticket issued successfully, remaining tickets: 8
    Preparing to sell tickets
    Tickets issued successfully, remaining tickets: 6
    Preparing to sell tickets
    Ticket issued successfully, remaining tickets: 4
    Preparing to sell tickets
    Tickets issued successfully, remaining tickets: 5
    Preparing to sell tickets
    Tickets issued successfully, remaining tickets: 2
    Preparing to sell tickets
    Ticket issued successfully, remaining tickets: 1
    Preparing to sell tickets
    Ticket issued successfully, remaining tickets: 3
    Preparing to sell tickets
    Ticket issued successfully, remaining tickets: 0
    Successful ticket issuance, remaining tickets:-2
    Successful ticket issuance, remaining tickets:-1
 Note: the result is not unique

Thread safety

Thread synchronization

Synchronous code block (implicit lock)

Thread unsafe solution 1. synchronized

Format: synchronized (lock object) {}

Lock object: any object in Java can be regarded as a lock object, and any object can be marked with a lock
Execution process: thread a executes to synchronized and marks this object as locked. Other threads need to execute synchronized and queue up. The lock mark is released before it can enter. After thread a executes synchronized, all threads compete for synchronized.

public static void main(String[] args) {
        // Thread unsafe
        // Solution 1. Synchronize code blocks
        // Format: synchronized (lock object) {}
        Runnable r = new Ticket();
        new Thread(r).start();
        new Thread(r).start();
        new Thread(r).start();
    }

    private static class Ticket implements Runnable {
        // Number of votes
        private int count = 10;
        private Object o = new Object();
        @Override
        public void run() {
            while (true) {
                // Selling tickets
                synchronized (o) {
                    if (this.count > 0) {
                        System.out.println("Preparing to sell tickets");
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        this.count--;
                        System.out.println(Thread.currentThread().getName() + "Successful ticket issuance, remaining tickets:" + this.count);
                    } else {
                        break;
                    }
                }
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

Output:
    Preparing to sell tickets
    Thread-0 Ticket issued successfully, remaining tickets: 9
    Preparing to sell tickets
    Thread-2 Ticket issued successfully, remaining tickets: 8
    Preparing to sell tickets
    Thread-1 Tickets issued successfully, remaining tickets: 7
    Preparing to sell tickets
    Thread-2 Tickets issued successfully, remaining tickets: 6
    Preparing to sell tickets
    Thread-0 Tickets issued successfully, remaining tickets: 5
    Preparing to sell tickets
    Thread-2 Ticket issued successfully, remaining tickets: 4
    Preparing to sell tickets
    Thread-1 Ticket issued successfully, remaining tickets: 3
    Preparing to sell tickets
    Thread-2 Tickets issued successfully, remaining tickets: 2
    Preparing to sell tickets
    Thread-0 Ticket issued successfully, remaining tickets: 1
    Preparing to sell tickets
    Thread-2 Ticket issued successfully, remaining tickets: 0

Note: the output is not unique

Thread synchronization method (implicit lock)

Encapsulate methods to ensure thread safety

The default lock of the synchronization method is this
The synchronization method is statically modified to lock to the class name. Class

Multiple synchronization methods use this lock. One of the methods is executed, and the other methods cannot be executed. They are all in a queued state.

this lock is used to add synchronous code blocks in the same thread. When synchronous code blocks or synchronous methods are executed, other methods and synchronous code blocks cannot be executed.

public static void main(String[] args) {
        // Synchronization method
        Runnable r = new Ticket();
        new Thread(r).start();
        new Thread(r).start();
        new Thread(r).start();
    }

    private static class Ticket implements Runnable {
        // Number of votes
        private int count = 10;
        @Override
        public void run() {
            while (true) {
                if (!sale()) {
                    break;
                }
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        private synchronized boolean sale() {
            // The lock is this, the current object
            // If a method is statically decorated, the method is the class name
            // Selling tickets
            if (this.count > 0) {
                System.out.println("Preparing to sell tickets");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.count--;
                System.out.println(Thread.currentThread().getName() + "Successful ticket issuance, remaining tickets:" + this.count);
                return true;
            }
            return false;
        }
    }
Output:
    Preparing to sell tickets
    Thread-0 Ticket issued successfully, remaining tickets: 9
    Preparing to sell tickets
    Thread-2 Ticket issued successfully, remaining tickets: 8
    Preparing to sell tickets
    Thread-1 Tickets issued successfully, remaining tickets: 7
    Preparing to sell tickets
    Thread-2 Tickets issued successfully, remaining tickets: 6
    Preparing to sell tickets
    Thread-0 Tickets issued successfully, remaining tickets: 5
    Preparing to sell tickets
    Thread-2 Ticket issued successfully, remaining tickets: 4
    Preparing to sell tickets
    Thread-1 Ticket issued successfully, remaining tickets: 3
    Preparing to sell tickets
    Thread-2 Tickets issued successfully, remaining tickets: 2
    Preparing to sell tickets
    Thread-0 Ticket issued successfully, remaining tickets: 1
    Preparing to sell tickets
    Thread-2 Ticket issued successfully, remaining tickets: 0

Lock (display lock)

ReentrantLock

public static void main(String[] args) {
        // Thread unsafe
        // Solution 3. Display Lock subclass ReentrantLock
        Runnable r = new Ticket();
        new Thread(r).start();
        new Thread(r).start();
        new Thread(r).start();
    }

    private static class Ticket implements Runnable {
        // Number of votes
        private int count = 10;
        // Show Lock lock
        private Lock l = new ReentrantLock();
        @Override
        public void run() {
            while (true) {
                l.lock();
                // Selling tickets
                if (this.count > 0) {
                    System.out.println("Preparing to sell tickets");
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    this.count--;
                    System.out.println(Thread.currentThread().getName() + "Successful ticket issuance, remaining tickets:" + this.count);
                } else {
                    break;
                }
                l.unlock();
            }
        }
    }
Output:
	Preparing to sell tickets
    Thread-2 Ticket issued successfully, remaining tickets: 9
    Preparing to sell tickets
    Thread-2 Ticket issued successfully, remaining tickets: 8
    Preparing to sell tickets
    Thread-2 Tickets issued successfully, remaining tickets: 7
    Preparing to sell tickets
    Thread-2 Tickets issued successfully, remaining tickets: 6
    Preparing to sell tickets
    Thread-2 Tickets issued successfully, remaining tickets: 5
    Preparing to sell tickets
    Thread-2 Ticket issued successfully, remaining tickets: 4
    Preparing to sell tickets
    Thread-2 Ticket issued successfully, remaining tickets: 3
    Preparing to sell tickets
    Thread-2 Tickets issued successfully, remaining tickets: 2
    Preparing to sell tickets
    Thread-2 Ticket issued successfully, remaining tickets: 1
    Preparing to sell tickets
    Thread-2 Ticket issued successfully, remaining tickets: 0

Fair lock - unfair lock

Fair lock

Need to queue
//Show Lock: if the Lock: Fair parameter is true, it means a fair Lock
Lock l = new ReentrantLock(fair:true);

Unfair lock

Grab and execute together

thread deadlock

Thread deadlock means that two or more threads hold the resources needed by each other. Due to the synchronized feature, a thread holds a resource or obtains a lock. Before the thread releases the lock, other threads cannot obtain the lock and will wait forever. Therefore, this leads to deadlock.

public static void main(String[] args) {
        // thread deadlock 
        Criminal c = new Criminal();
        Police p = new Police();
        new MyThread(c, p).start();
        c.say(p);
    }

    static class MyThread extends Thread {
        private Criminal c;
        private Police p;

        public MyThread(Criminal c, Police p) {
            this.c = c;
            this.p = p;
        }

        @Override
        public void run() {
            p.say(c);
        }
    }

    // police
    static class Criminal {
        public synchronized void say(Police p) {
            System.out.println("You let me go, I let the hostages go");
            p.fun();
        }

        public synchronized void fun() {
            System.out.println("The criminal was released and the criminal released the hostages");
        }
    }

    // criminal
    static class Police {
        public synchronized void say(Criminal c) {
            System.out.println("You released the hostages, I released you");
            c.fun();
        }
        public synchronized void fun() {
            System.out.println("The police saved the hostage, but the criminal ran away");
        }
    }
Output:
    You let me go, I let the hostages go
	You released the hostages, I released you

Multithreaded communication

Object

wait

public final void wait() throws InterruptedException
     Causes the current thread to wait for it to wake up, usually a notification or interrupt.
public final void wait​(long timeoutMillis) throws InterruptedException
     Causes the current thread to wait for it to wake up, usually a notification or interrupt, or until a certain amount of real-time.
public final void wait​(long timeoutMillis, int nanos) throws InterruptedException
     Causes the current thread to wait for it to wake up, usually a notification or interrupt, or until a certain amount of real-time.

notify,notifyAll

public final void notify()
     Wake up a single thread waiting on this object monitor. If any thread is waiting for this object, select one of the threads to wake up. The choice is arbitrary

public final void notifyAll()
     Wake up all threads waiting for this object monitor.

/**
     * Producer and Consumer 
     *
     * @param args
     */
    public static void main(String[] args) {
        Food f = new Food();
        new Cook(f).start();
        new Waiter(f).start();
    }

    /**
     * Chef internal class
     */
    private static class Cook extends Thread{
        private Food f;
        public Cook(Food f){
            this.f = f;
        }

        @Override
        public void run() {
            for (int i = 0; i < 100; i++){
                if (i % 2 == 0){
                    f.setNameAndTaste("Liao Jing little villain", "fool");
                } else {
                    f.setNameAndTaste("Hong Hai's father", "clever");
                }
            }
        }
    }

    /**
     * Waiter internal class
     */
    private static class Waiter extends Thread{
        private Food f;
        public Waiter(Food f){
            this.f = f;
        }

        @Override
        public void run() {
            for (int i = 0; i < 100; i++){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                f.get();
            }
        }
    }

    /**
     * Food internal class
     */
    private static class Food {
        private String name; // Food name
        private String taste; // Food taste
        // When flag = true, the cook cooks,
        // When flag = false, the waiter takes the meal
        private boolean flag = true;
        public synchronized void setNameAndTaste(String name, String taste) {
            if (flag) {
                this.name = name;
                // Sleep for 100 milliseconds
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.taste = taste;
                flag = false;
                // Wake up the waiter
                this.notifyAll();
                // Cook sleep
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        public synchronized void get(){
            if (!flag) {
                System.out.println("Food name;" + this.name + ",Taste:" + this.taste);
                flag = true;
                // Wake up the cook
                this.notifyAll();
                // Waiter sleep
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
Output:
    Food name; Liao Jing, little villain, taste: fool
	Food name; Honghai dad, taste: smart
    Food name; Liao Jing, little villain, taste: fool
    ......
    Food name; Honghai dad, taste: smart
    Food name; Liao Jing, little villain, taste: fool
    Food name; Honghai dad, taste: smart

Six states of threads

Enum Thread.state

Enum Constantdescribe
NEWThe thread state of a thread that has not been started.
RUNNABLEStatus of runnable threads
BLOCKEDThe thread state of the thread is blocked waiting for the monitor to lock.
WAITINGThread state of the waiting thread
TIMED_WAITINGThe thread state of the waiting thread with the specified waiting time.
TERMINATEDThread state of the terminating thread

Benefits of thread pooling

  • Reduce resource consumption

  • Increase the corresponding speed

  • Improve thread manageability

Four thread pools in Java. ExecutorService

1. Cache thread pool

2. Fixed length linear pool

3. Single thread pool

4. Periodic task fixed length routing pool

Tags: Java

Posted on Wed, 15 Sep 2021 16:30:30 -0400 by MarkB423