JAVA multithreading (learning notes) (difference between Thread and Runnable, anonymous inner class)

Posted by psd99 on Sat, 04 Sep 2021 23:08:15 +0200

The difference between Thread and Runnable

Benefits of implementing Runnable interface to create multithreaded programs:

  1. Avoid the limitation of single inheritance;
    A class can only have one parent class. If a class inherits Thread class, it cannot inherit other classes
    Therefore, the Runnable interface is implemented. You can also inherit other classes and implement other interfaces.
  2. It enhances the expansibility of the program and reduces the coupling of the program (decoupling);
    The way of realizing Runnable interface separates (decouples) setting thread tasks from starting new threads;
    In the implementation class, the run method is rewritten: used to set thread tasks;
    Create a Thread class object and call the start method: used to start a new Thread.
    public static void main(String[] args) {
//        RunnableImpl  run = new RunnableImpl();
        Thread t = new Thread(new RunnableImpl2());//4. Create a Thread class object and construct the implementation class object passing the Runnable interface in the method
        t.start();

        for (int i = 0; i <20 ; i++) {
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
    }

The implementation class of the Runnable interface passed in the Thread constructor will run the Thread.

Anonymous inner classes implement multithreading

The role of anonymous inner classes: simplifying code
The sub class inherits the parent class, overrides the parent class method, and creates a sub class object
The implementation class interface, rewriting the methods in the interface and creating the implementation class object are combined in one step
The end product of anonymous inner classes: subclass / implementation class objects, but this class has no name
Format:

new Parent class/Interface(){
		Override parent class/Methods in interfaces
};
public static void main(String[] args) {
        //The parent class of a Thread is Thread
        //new MyThread().start();
        new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 20; i++) {
                    System.out.println(Thread.currentThread().getName()+"-->"+"Override thread method of anonymous inner class");
                }
            }
        }.start();

        //Thread interface Runnable
        //Runnable r = new RunnableImpl();
        Runnable r = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 20; i++) {
                    System.out.println(Thread.currentThread().getName()+"-->"+"Override thread method of anonymous inner class");
                }
            }
        };
        new Thread(r).start();
    }

Thread safety issues

My understanding is that there are problems when multiple threads rob shared resources.
For example, the cinema has three ticket windows for ticket selling at the same time. When three people go to each window to buy tickets, they rob one ticket at the same time, which can be understood as thread insecurity.
For example, create an implementation class of Runnable, and the run method can sell 100 tickets;

public class RunnableImpl implements Runnable {
    private int ticket = 100;

    @Override
    public void run() {
        while(true) {
            if (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + "Thread is selling" + ticket + "Ticket");
                ticket--;
            }
        }
    }
}

Create 3 Thread objects to grab the same resource:
Note: here is to create an implementation class object and use three thread objects to snatch 100 tickets. If you create three implementation class objects, it is 300 tickets, and there is no problem of seizing shared resources.

public static void main(String[] args) {
        Runnable run = new RunnableImpl();
        Thread t0 = new Thread(run);
        Thread t1 = new Thread(run);
        Thread t2 = new Thread(run);
        t0.start();
        t1.start();
        t2.start();
    }

When running, you will find that threads are unsafe, and the three threads rob common resources.


Using this diagram, we can understand why the same output results occur.

Resolve thread safety issues

1. Synchronized code block synchronized

Format:

synchronized(Lock object){
		Thread safe code may occur (shared resources accessed)
}

be careful:

  1. Through the lock object in the code block, any object can be used;
  2. It must be ensured that the lock object used by multiple threads is the same;
  3. Lock object function:
    Lock the synchronization code block and let only one thread execute in the synchronization code block.
public class RunnableImpl implements Runnable {
    private int ticket = 100;

    Object obj = new Object();
    @Override
    public void run() {
        while(true) {
            synchronized (obj){
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + "Thread is selling" + ticket + "Ticket");
                    ticket--;
                }
            }
        }
    }
}

Locking can ensure that only one thread in the synchronization code block is executing the task. Only when its thread returns the lock object after executing the task, the three objects continue to rob the lock object (cpu execution right). Synchronization ensures security, but the efficiency will be reduced if the program frequently judges, obtains and releases locks.

2. Synchronization method

Format:

public synchronized void method(){
		Code that may cause thread safety problems
}

The lock object used by the synchronization method is actually the implementation class object( (i.e. this type of object)
The synchronization method can also be static:
Format:

public static synchronized void method(){
		Code that may cause thread safety problems
}

However, the lock object used by the static synchronization method is not the implementation class object, because this is generated after the object is created, and the static method takes precedence over the object. The lock object of the static method is the class attribute of this class, that is, the class file object. Equivalent to:

synchronized(RunnableImpl.class){
		Thread safety problem code block
}

3. Lock lock

Methods in the Lock interface: void lock() to obtain the Lock and void unlock() to release the Lock
java.util.concurrent.locks.ReentrantLock implements Lock interface

Usage: create a ReentrantLock object in the member's location, and call the lock method to get the lock before the code that may have security problems. After that, call the unlock method to release the lock.

Thread state

Six thread statuses can be viewed in the API:

(blogging is really time-consuming and laborious, sobbing)

Waiting for wake-up (steamed stuffed bun shop example)

Customer thread (consumer): inform the boss of the type and quantity of steamed stuffed bun, call the wait method, give up the execution of cpu, and enter the WAITING state (infinite WAITING).
Boss thread (producer): spend a period of time making buns, and then call the notify method to wake customers to eat baozi.

public static void main(String[] args) {
        Object obj = new Object();

        new Thread(){
            @Override
            public void run() {
                synchronized (obj){
                    System.out.println("Customer 1 told the boss the type and quantity of steamed buns he wanted");
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println("Awakened customer 1 starts eating steamed stuffed buns");
                }
            }
        }.start();

        new Thread(){
            @Override
            public void run() {
                synchronized (obj){
                    System.out.println("It takes five seconds to start making steamed stuffed buns");
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println("The steamed stuffed bun is ready. Go to wake up the customers~");
                    obj.notify();
                }
            }
        }.start();
    }


Note the notify method: waking up a single thread waiting on this object monitor (object lock) will continue to execute the code after the wait method.

obj.notify(); // If there are multiple threads, wake up one at random
obj.notifyAll(); // Wake up all waiting threads

There are two ways to enter TimeWaiting:

  1. Using the sleep(long m) method, after the end of the millisecond value, the thread wakes up and enters the Runnable/Blocked state;
  2. Use the wait(long m) method. If the wait method is not awakened by notify after the end of the millisecond value, it will wake up automatically.

Woo woo, let's write here first.

Topics: Java