Java learning day022-025 (multithreading-B station dark horse version)

Posted by thewitt on Sun, 19 Dec 2021 22:14:52 +0100

1. Processes and threads

process

Process: is a running program

  • It is an independent unit for the system to allocate and call resources
  • Each process has its own memory space and system resources

thread

Thread: it is a single sequential control flow in a process and an execution path

  • Single thread: if a process has only one execution path, it is called a single threaded program
  • Multithreading: if a process has multiple execution paths, it is called a multithreaded program

give an example

  • Notepad program
  • Mine clearance procedure

2. Implement multithreading by inheriting Thread class

Method 1: inherit Thread class

  • Define a class MyThread to inherit the Thread class
  • Override the run() method in the MyThread class
  • Create an object of the MyThread class
  • Start thread

Two small problems:

  • Why override the run() method?
    • Because run() is used to encapsulate the code executed by the thread
  • What is the difference between the run() method and the start() method?
    • run(): encapsulates the code executed by the thread. It is called directly, which is equivalent to the call of ordinary methods
    • start(): start the thread; The run() method of this thread is then called by the JVM
package com.study_01;

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(i);
        }
    }
}
package com.study_01;

public class MyThreadDemo {
    public static void main(String[] args) {
        MyThread my1 = new MyThread();
        MyThread my2 = new MyThread();

//        my1.run();
//        my2.run();

        // void start() causes this thread to start execution; The Java virtual machine calls the run method of this thread.
        my1.start();
        my2.start();
    }
}

3. Set and get thread name

Method for setting and getting Thread name in Thread class

  • void setName (String name): change the name of this thread to be equal to the parameter name
  • String getName(): returns the name of this thread
  • ·The thread name can also be set through the construction method

How to get the thread name where the main() method is located?

  • public static Thread currentThread(): returns a reference to the currently executing thread object
package com.study_02;

public class MyThread extends Thread{
    public MyThread() {
    }

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

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName()+":"+i);
        }
    }
}

/*
    public Thread() {
        this(null, null, "Thread-" + nextThreadNum(), 0);
    }

    public Thread(ThreadGroup group, Runnable target) {
        this(group, target, "Thread-" + nextThreadNum(), 0);
    }

    private static int threadInitNumber; // 0
    private static synchronized int nextThreadNum() {
            return threadInitNumber++;
    }

 */
package com.study_02;

public class MyThreadDemo {
    public static void main(String[] args) {
        MyThread my1 = new MyThread();
        MyThread my2 = new MyThread();
//
//        //Thread renaming
//        my1.setName("high speed rail");
//        my2.setName("aircraft");

        // Thread(String name)
//        MyThread my1 = new MyThread("high speed rail");
//        MyThread my2 = new MyThread("aircraft");
//
//        my1.start();
//        my2.start();

        // static Thread currentThread() returns a reference to the thread object currently executing.
        System.out.println(Thread.currentThread().getName());
    }
}

4. Thread priority

Threads have two scheduling models

  • Time sharing scheduling model: all threads use the right to use the CPU in turn, and evenly allocate the time slice of CPU occupied by each thread
  • Preemptive scheduling model: give priority to the threads with high priority to use CPU. If the threads have the same priority, one will be selected randomly. The threads with high priority will obtain more CPU time slices

If the computer has only one CPU, then the CPU can only execute one instruction at a certain time. Only when the thread gets the CPU time slice, that is, the right to use, can it execute the instruction. Therefore, the execution of multithreaded programs is random, because it is not certain who grabs the right to use the CPU

Method for setting and obtaining Thread priority in Thread class

  • public final int getPriority(): returns the priority of this thread
  • Public final void setpriority (int newPriority): change the priority of this thread

The default priority of thread is 5; The range of thread priority is: 1-10. High thread priority only means that the thread has a high probability of obtaining CPU time slice, but you can see the effect you want when it is run more times or more times

package com.study_03;

public class ThreadPriority extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName()+":"+i);
        }
    }
}
package com.study_03;

public class ThreadPriorityDemo {
    public static void main(String[] args) {
        ThreadPriority tp1 = new ThreadPriority();
        ThreadPriority tp2 = new ThreadPriority();
        ThreadPriority tp3 = new ThreadPriority();

        tp1.setName("high-speed rail");
        tp2.setName("aircraft");
        tp3.setName("automobile");

        // Get thread priority
//        System.out.println(tp1.getPriority()); // 5
//        System.out.println(tp2.getPriority()); // 5
//        System.out.println(tp3.getPriority()); // 5

        // Change thread priority
//        tp1.setPriority(10000); // IllegalArgumentException

//        System.out.println(ThreadPriority.MAX_PRIORITY); // 10
//        System.out.println(ThreadPriority.MIN_PRIORITY); // 1
//        System.out.println(ThreadPriority.NORM_PRIORITY); // 5

        // Set the correct priority
        tp1.setPriority(5);
        tp2.setPriority(10);
        tp3.setPriority(1);


        tp1.start();
        tp2.start();
        tp3.start();


    }
}

5. Thread control

6. Thread life cycle

7. Implement Runnable interface

Method 2: implement Runnable interface

  • Define a class MyRunnable to implement the Runnable interface
  • Override the run() method in the MyRunnable class
  • Create an object of the MyRunnable class
  • Create an object of Thread class and take the MyRunnable object as the parameter of the construction method
  • Start thread

There are two implementation schemes of multithreading

  • Inherit Thread class
  • Implement Runnable interface

Advantages of implementing the Runnable interface over inheriting the Thread class

  • It avoids the limitation of Java single inheritance
  • It is suitable for multiple codes of the same program to deal with the same resource, and effectively separates the thread from the program code and data, which better reflects the object-oriented design idea
package com.study_05;

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
package com.study_05;

public class MyRunnableDemo {
    public static void main(String[] args) {
        // Create an object of the MyRunnable class
        MyRunnable my = new MyRunnable();

        // Create an object of Thread class and take the MyRunnable object as the parameter of the construction method
        // Thread(Runnable target) assigns a new thread object.
//        Thread t1 = new Thread(my);
//        Thread t2 = new Thread(my);

        // Thread(Runnable target, String name)
        Thread t1 = new Thread(my,"high-speed rail");
        Thread t2 = new Thread(my,"aircraft");

        //Start thread
        t1.start();
        t2.start();

    }
}

8. Selling tickets

Thread synchronization

package com.study_06;

public class SellTicket implements Runnable {
    private int tickets = 100;

    @Override
    public void run() {
        while (true) {
                if (tickets > 0) {
                    // Simulate the ticket selling time through the Sleep() method class
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "The second is being sold" + tickets + "Ticket");
                    tickets--;

            }
        }
    }
}
package com.study_06;

public class SellTicketDemo {
    public static void main(String[] args) {
        // Create an object of SellTicket class
        SellTicket st = new SellTicket();

        // Create three Thread class objects, take the SellTicket object as the parameter of the construction method, and give the corresponding window name
        Thread t1 = new Thread(st,"Window 1");
        Thread t2 = new Thread(st,"Window 2");
        Thread t3 = new Thread(st,"Window 3");

        // Start thread
        t1.start();
        t2.start();
        t3.start();
    }
}

9. Reflections on ticket selling cases

10. Synchronize code blocks to solve data security problems

Why is there a problem? (this is also the standard for us to judge whether there will be data security problems in multithreaded programs

  • Is it a multithreaded environment
  • Is there shared data
  • Are there multiple statements that operate on shared data

How to solve the problem of multithreading security?

  • Basic idea: let the program have no security environment

How?

  • Lock the code for multiple statements to operate the shared data, so that only one thread can execute at any time
  • Java provides a way to synchronize code blocks to solve k

Synchronous code block
Locking multiple statements to operate on shared data can be implemented using synchronous code blocks

  • Format:
synchronized(Any object){
    Code for multiple statements to operate on shared data
}
  • synchronized (arbitrary object): it is equivalent to locking the code, and any object can be regarded as a lock

Advantages and disadvantages of synchronization

  • Benefits: it solves the data security problem of multithreading
  • Disadvantages: when there are many threads, each thread will judge the lock on synchronization, which is very resource-consuming and will virtually reduce the running efficiency of the program
package com.study_08;

public class SellTicket implements Runnable {
    private int tickets = 100;
    private Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            synchronized (obj) {
                if (tickets > 0) {
                    // Simulate the ticket selling time through the Sleep() method class
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "The second is being sold" + tickets + "Ticket");
                    tickets--;
                }
            }
        }
    }
}
package com.study_08;

public class SellTicketDemo {
    public static void main(String[] args) {
        // Create an object of SellTicket class
        SellTicket st = new SellTicket();

        // Create three Thread class objects, take the SellTicket object as the parameter of the construction method, and give the corresponding window name
        Thread t1 = new Thread(st,"Window 1");
        Thread t2 = new Thread(st,"Window 2");
        Thread t3 = new Thread(st,"Window 3");

        // Start thread
        t1.start();
        t2.start();
        t3.start();
    }
}

11. The synchronization method solves the problem of data security

Synchronization method: add the synchronized keyword to the method

  • Format:
    • Modifier synchronized return value type method name (method parameter) {}

What is the lock object of the synchronization method?

  • this

Synchronous static method: add the synchronized keyword to the static method

  • Format:
    • Modifier static synchronized return value type method name (method parameter) {}

What is the lock object for synchronous static methods?

  • Class name class
package com.study_09;

public class SellTicket implements Runnable {
    //    private int tickets = 100;
    private static int tickets = 100;
    private Object obj = new Object();
    private int x = 0;

    @Override
    public void run() {
        while (true) {
            if (x % 2 == 0) {
//                synchronized (obj) {
//                synchronized (this) {
                synchronized (SellTicket.class) {
                    if (tickets > 0) {
                        // Simulate the ticket selling time through the Sleep() method class
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "The second is being sold" + tickets + "Ticket");
                        tickets--;
                    }
                }
            } else {
//                synchronized (obj) {
//                    if (tickets > 0) {
//                        //Simulate the ticket selling time through the Sleep() method class
//                        try {
//                            Thread.sleep(100);
//                        } catch (InterruptedException e) {
//                            e.printStackTrace();
//                        }
//                        System.out.println(Thread.currentThread().getName() + "selling" + tickets + "ticket");
//                        tickets--;
//                    }
//                }
                SellTicket();
            }
            x++;
        }
    }

    //    public void SellTicket() {
//        synchronized (obj) {
//            if (tickets > 0) {
//                //Simulate the ticket selling time through the Sleep() method class
//                try {
//                    Thread.sleep(100);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//                System.out.println(Thread.currentThread().getName() + "selling" + tickets + "ticket");
//                tickets--;
//            }
//        }
//    }
    // Lock on method
//    public synchronized void SellTicket() {
//        if (tickets > 0) {
//            //Simulate the ticket selling time through the Sleep() method class
//            try {
//                Thread.sleep(100);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            System.out.println(Thread.currentThread().getName() + "selling" + tickets + "ticket");
//            tickets--;
//        }
//    }
    public static synchronized void SellTicket() {
        if (tickets > 0) {
            // Simulate the ticket selling time through the Sleep() method class
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "The second is being sold" + tickets + "Ticket");
            tickets--;
        }
    }
}
package com.study_09;

public class SellTicketDemo {
    public static void main(String[] args) {
        // Create an object of SellTicket class
        SellTicket st = new SellTicket();

        // Create three Thread class objects, take the SellTicket object as the parameter of the construction method, and give the corresponding window name
        Thread t1 = new Thread(st,"Window 1");
        Thread t2 = new Thread(st,"Window 2");
        Thread t3 = new Thread(st,"Window 3");

        // Start thread
        t1.start();
        t2.start();
        t3.start();
    }
}

12. Thread safe classes

StringBuffer

  • Thread safe, variable character sequence
  • Starting from version JDK5, it is replaced by StringBuilder. The StringBuilder class should usually be used because it supports all the same operations, but it is faster because it does not perform synchronization

Vector

  • From Java 2 platform v1 2, this class improved the List interface to become a member of the Java Collections Framework. Unlike the new collection implementation, the Vector is synchronized. If a thread safe implementation is not required, it is recommended to use ArrayList instead of Vector

Hashtable

  • This class implements a hash table that maps keys to values. Any non nul object can be used as a key or value
  • From Java 2 platform v1 2, the class was improved to implement the Map interface and make it a member of the Java Collections Framework.
    Unlike the new collection implementation, hashtables are synchronized. If thread safe implementation is not required, it is recommended to use HashMap instead of Hashtable
package com.study_10;

import java.util.*;

public class ThreadDemo {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer();
        StringBuilder sb1 = new StringBuilder();

        Vector<String> v = new Vector<>();
        ArrayList<String> arrayList = new ArrayList<>();

        Hashtable<String ,String> ht = new Hashtable<>();
        HashMap<String,String> hm = new HashMap<>();

        // static <T> List<T> synchronizedList(List<T> list)
        // Returns a list of synchronization (thread safe) supported by the specified list
        List<String> list = Collections.synchronizedList(new ArrayList<String>());

    }
}

13.Lock lock

Although we can understand the lock object problem of synchronization code blocks and synchronization methods, we do not directly see where the lock is added and where the lock is released for clearer expression

How to add and release locks? JDK5 provides a new Lock object Lock

The Lock implementation provides a wider range of locking operations than using synchronized methods and statements

Lock provides methods for obtaining and releasing locks

  • void lock(): get lock
  • void unlock(): release the lock

Lock is an interface that cannot be instantiated directly. Its implementation class ReentrantLock is used to instantiate it here

Construction method of ReentrantLock

  • Reentrantlock(): create an instance of ReentrantLock
package com.study_11;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SellTicket implements Runnable {
    private int tickets = 100;
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();
                if (tickets > 0) {
                    // Simulate the ticket selling time through the Sleep() method class
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "The second is being sold" + tickets + "Ticket");
                    tickets--;
                }
            } finally {
                lock.unlock();
            }
        }
    }
}
package com.study_11;

public class SellTicketDemo {
    public static void main(String[] args) {
        // Create an object of SellTicket class
        SellTicket st = new SellTicket();

        // Create three Thread class objects, take the SellTicket object as the parameter of the construction method, and give the corresponding window name
        Thread t1 = new Thread(st,"Window 1");
        Thread t2 = new Thread(st,"Window 2");
        Thread t3 = new Thread(st,"Window 3");

        // Start thread
        t1.start();
        t2.start();
        t3.start();
    }
}

14. Producer consumer model

Producer consumer model is a very classic multi-threaded cooperation model. Understanding the producer consumer problem can make us have a deeper understanding of multi-threaded programming

In fact, there are two types of threads:

  • One is the producer thread used to produce data
  • One is consumer thread, which is used to consume data

In order to decouple the relationship between producers and consumers, shared data areas are usually used, just like a warehouse

  • The producer's production data is directly placed in the shared data area, and does not need to care about the behavior of consumers
  • Consumers only need to obtain data from the shared data area, and do not need to care about the behavior of producers

In order to reflect the wait and wake-up during production and consumption, Java provides several methods for us to use. These methods are the wait and wake-up methods of Object class in Objec class:

15. Producer consumer cases

package com.study_12;

public class Box {
    // Define milk quantity
    private int milk;
    // Define a member variable to represent the status of the milk box
    private boolean state = false;

    // Provides the operation of storing milk
    public synchronized void put(int milk) {
        // If there is milk, wait for consumption
        if (state) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // If not, produce milk
        this.milk = milk;
        System.out.println("The milkman will"+this.milk+"Put a bottle of milk into the milk box");

        // After production, modify the milk box status
        state = true;

        // awaken
        notifyAll();

    }

    public synchronized void get() {
        // If not, wait for production
        if(!state) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // Consume if you have
        System.out.println("The user gets the second"+this.milk+"Bottle milk");
        state = false;
        // awaken
        notifyAll();
    }

}
package com.study_12;

public class Customer implements Runnable{
    private Box b;
    public Customer(Box b) {
        this.b = b;
    }

    @Override
    public void run() {
        while (true){
            b.get();
        }

    }
}
package com.study_12;

public class Producer implements Runnable{
    private  Box b;
    public Producer(Box b) {
        this.b = b;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            b.put(i);
        }
    }
}
package com.study_12;

public class BoxDemo {
    public static void main(String[] args) {
        // Create a milk box formation, which is a shared data area
        Box b = new Box();

        // Create a producer object and pass the milk box object as a constructor parameter, because the operation to store milk is called in this class
        Producer p = new Producer(b);
        // Create a consumer object and pass the milk box object as a constructor parameter, because the operation to store milk is called in this class
        Customer c = new Customer(b);

        // Create two thread objects
        Thread t1 = new Thread(p);
        Thread t2 = new Thread(c);

        // Start thread
        t1.start();
        t2.start();

    }
}

Topics: Java