Multi thread production and consumption, semaphore method, pipe method, thread lock and wake-up mechanism

Introduction to threads and processes

Thread: an application that runs in memory. Each process has its own independent memory space. A process can have multiple threads. For example, in Windows system, a running XX Exe is a process.

Process: an execution task (control unit) in a process, which is responsible for the execution of programs in the current process. A process can run multiple threads, but there is at least one thread in a process, otherwise it has no meaning. Multiple threads can share data. Thread is the smallest unit of CPU scheduling and execution.

Benefits of multithreading:

Can improve CPU utilization. In multithreaded programs, when a thread has to wait, the CPU can run other threads instead of waiting, which greatly improves the efficiency of the program. In other words, a single program is allowed to create multiple threads executing in parallel to complete their respective tasks

Disadvantages:

-Threads are also programs, so threads need to occupy memory. The more threads, the more memory they occupy;
-Multithreading needs coordination and management, so CPU time is required to track threads;
-The access to shared resources between threads will affect each other, and the problem of competing for shared resources must be solved

Calling the run() method is different from calling the start() method


The start() method is used to start the thread, and the run() method is used to execute the runtime code of the thread. run() can be called repeatedly, while start() can only be called once.

start() method to start a Thread, which really realizes multi-threaded operation. Calling the start() method does not need to wait for the execution of the body code of the run method, but can directly continue to execute other codes; At this time, the Thread is in the ready state and is not running. Then, the Thread class calls the method run() to complete its running state. The run() method ends and the Thread terminates. Then the CPU schedules other threads.

The run() method is in this thread. It is only a function in the thread, not multithreaded. If you call run() directly, it is equivalent to calling an ordinary function. The run() method to be used directly must wait for the execution of the run() method to execute the following code. Therefore, there is only one execution path and there is no thread feature at all. Therefore, the start() method should be used instead of the run() method during multithreading execution.
 

2. Four ways to create threads

1. Inherit Thread class

2. Implement Runnable interface

3.Callaable interface

4. Create thread pool

1. Inherit Thread class

  1. Inherit Thread class
  2. Override the run() method
  3. Create a thread object and call the start() method to start

Not recommended: avoid the limitation of oop single inheritance

 2. Implement Runnable interface

  1. Implement Runnable interface
  2. Override the run() method
  3. Create a thread object and start the thread through the thread object
  4. Call the stert() method

Recommended: it avoids the limitation of single inheritance, is flexible and convenient, and is convenient for the same object to be used by multiple threads

 3. Implement Callable interface

  1. Implement the callable interface
  2. Override the call() method, which has a return value
  3. Create and execute services through threads
  4. Get thread return value
  5. Close return value

Static proxy

You: what a role

Wedding company: represent you and deal with the marriage

Marriage: just implement the marriage interface

It's true that both object and proxy object should implement the same interface

The proxy object should represent the real role

The advantage is that the proxy object can do many things that the real object can't do

Real objects focus on doing their own things

//summary
//Both real objects and proxy objects should implement the same interface
//The proxy object should represent the real role


//Advantage: the advantage is that the proxy object can do many things that the real object can't do
//Real objects focus on doing their own things
public class StaticProxy {
    public static void main(String[] args) {
        You you = new You(); //Your marriage

        new Thread(() -> System.out.println("love you")).start();
        new WeddingCompany(new You()).HappyMarry();

//        WeddingCompany windowConstants = new WeddingCompany( you);
        windowConstants.HappyMarry();

    }

}

interface  Marry{
    void HappyMarry();
}

//Real role
class You implements Marry {

    public void HappyMarry() {
        System.out.println("Get married!!!");
    }
}

//Acting role to help you get married
class WeddingCompany implements Marry {
//    Agent who -- > real target role
    private Marry target;

    public WeddingCompany(Marry target) {
        this.target = target;
    }

    public void HappyMarry() {
        befotr();
        this.target.HappyMarry();//Real object call
        after();
    }

    private void befotr() {
        System.out.println("Before marriage");
    }

    private void after() {
        System.out.println("after marriage i");
    }
}

Status of the thread

New thread() -- > create state -- > call start() -- > ready state -- > wait for cpu scheduling -- > running state -- > dead state

New thread() -- > create state call start() -- > ready state (wait for cpu scheduling, call sleep or wait or synchronization lock) - > blocking state (re-enter the ready state after blocking, wait for cpu scheduling) - -- > ready state -- > running state (enter the running state, the thread will actually execute the code block of the thread body) - > death state (the thread is interrupted or terminated. Once it enters the dead state, it cannot be started again)

Thread method

Stop thread

It is recommended that the thread stop itself

It is recommended to use a flag bit to terminate the variable, put flag=false, and the thread terminates

package state;

public class TestStop implements Runnable {
    //    1. Set an identification bit
    private boolean flag = true;
    @Override
    public void run() {
        int i = 0;
        while (flag) {
            System.out.println("run...Thread" + i);
        }
    }

    //    Set the exposed method to stop the thread and convert the bit ID
    public void stop() {
        this.flag = false;
    }
    public static void main(String[] args) {
        TestStop testStop = new TestStop();
        new Thread(testStop).start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("main" + i);
            if (i == 900) {
//                Call the stop method to switch the flag bit and stop the thread
                testStop.stop();
                System.out.println("Thread stop");
            }
        }
    }
}

Thread sleep

  1. sleep(1000) specifies the current thread blocking time of 1000 milliseconds
  2. Exception in sleep: InterruptedException
  3. When the sleep time arrives, the thread enters the ready state
  4. sleep can simulate network delay
  5. Every object has a lock, and sleep will not release the lock
package state;

//Past current system time
import java.text.SimpleDateFormat;
import java.util.Date;


public class TestSleep  {

    public static void main(String[] args) {
//        Print current time
        Date startTime = new Date(System.currentTimeMillis()); // Get current system time
        while (true) {
            try {
                Thread.sleep(1000);
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
                Date date = new Date(System.currentTimeMillis());//Update current time
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }}

//Analog countdown
public class TestSleep  {
    public static void main(String[] args) {
        try {
            tenDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void tenDown() throws InterruptedException {
        int num = 10;
        while (true) {
            Thread.sleep(1000);
            System.out.println(num--);
            if (num <= 0) {
                break;
            }
        }
    }
}

Thread comity

  1. Pause the currently executing thread without blocking it
  2. The thread changes from running state to ready state
  3. Rescheduling the CPU may not succeed
package state;

//Comity test
public class TestYield {
    public static void main(String[] args) {
        Myyield myyield = new Myyield();
        new Thread(myyield, "a").start();
        new Thread(myyield, "b").start();

    }
}

class Myyield implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "Start execution");
        Thread.yield(); //Comity
        System.out.println(Thread.currentThread().getName()+"Thread stop");
    }
}

 Join

Join merge threads, wait for this thread to finish executing, and then execute other threads, and other threads will block (think about jumping in line)

package state;

//Test Join
public class TestJoin implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.err.println("vip insert-----Team" + i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
//        Start thread
        TestJoin testStop = new TestJoin();
        Thread thread = new Thread(testStop);


//        Main thread
        for (int i = 0; i < 1000; i++) {
            if (i == 200) {
                thread.start();
                thread.join();//Cut in line
            }
            System.out.println("main" + i);
        }
    }
}

Thread state observation

Thread.State

package state;

//Observe thread status
public class TestState {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("******");
        });

//        Observation state
        Thread.State state = thread.getState();
        System.out.println(state); // NEW
//        Observe after startup
        thread.start();//Start thread
        state = thread.getState();
        System.out.println(state); //Run

        while (state != Thread.State.TERMINATED) { //As long as the thread does not terminate, it will always output the state
            Thread.sleep(100);
            state = thread.getState(); //Update thread status
            System.out.println(state); //Output status
        }
//        thread.start(); // A thread can only be started once. It cannot be started again after the thread dies
    }
}

Thread priority

java provides a thread scheduler to monitor all threads that enter the ready state after startup. The thread scheduler determines which thread should be scheduled to execute according to priority

The priority is expressed in numbers, from 0 to 10

  1. MAX_PRIORITY is 10, which is the highest priority of the thread;
  2. MIN_ The priority value is 1, which is the lowest priority of the thread;
  3. NORM_ The priority value is 5, which is the priority of the middle position of the thread

Use getPriority(),setPriority(int x), get and change the thread priority

Low thread priority only means that the probability of obtaining scheduling is low, not that it will not be adjusted if the priority is low. It depends on the scheduling of CPU. It is possible that CPU will schedule those with low priority (performance inversion)

package state;

//Test thread priority
public class TestPriority extends Thread{
    public static void main(String[] args) {
//        The priority of main thread is 5 by default
        System.out.println(Thread.currentThread().getName() + "----" + Thread.currentThread().getPriority());

        MyPriority myPriority = new MyPriority();
        Thread t1 = new Thread(myPriority);
        Thread t2 = new Thread(myPriority);
        Thread t3 = new Thread(myPriority);
        Thread t4 = new Thread(myPriority);
        Thread t5 = new Thread(myPriority);

//        Set priority at startup
        t1.start();

        t2.setPriority(1);
        t2.start();

        t3.setPriority(4);
        t3.start();

        t4.setPriority(Thread.MAX_PRIORITY); //MAX_PRIORITY == 10
        t4.start();

        t5.setPriority(11);
        t5.start();
    }
}

class MyPriority implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "----" + Thread.currentThread().getPriority());

    }
}

Daemon thread

  1. Threads are divided into user threads and daemon threads
  2. The virtual machine must ensure that the user thread (main()) is completed
  3. The virtual machine does not have to wait for the daemon thread (gc()) to finish executing
  4. Record operation logs in the background, monitor memory, garbage collection, etc
package state;

import java.util.concurrent.Callable;

//Test the daemon thread, and God will guard you
public class TestDeamo {
    public static void main(String[] args) {
        God god = new God();
        You you = new You();

        Thread thread = new Thread(god);
        thread.setDaemon(true);//The default is false, which means user threads. Normal threads are user threads

        thread.start(); //God guard start

        new Thread(you).start();//You start the y user thread
    }
}

//lord
class God implements Runnable {

    @Override
    public void run() {
        while (true) {
            System.out.println("God is with you");
        }
    }
}
//you
class You implements Runnable {


    @Override
    public void run() {
        for (int i = 0; i < 365000; i++) {
            System.err.println("You live happily all your life");
        }
        System.err.println("goodby world");
    }
}

Thread synchronization

Multiple threads operate on the same resource

Concurrency: the same object is operated by multiple threads at the same time (buying tickets)

Thread synchronization:

In the display situation, the most natural solution to the problem of "using one resource and multiple people want to use it" is queuing (queue)

When dealing with multithreading, when multiple threads access the same object (concurrent), and some threads want to modify the object, thread synchronization is required. Thread synchronization is actually a waiting mechanism. Multiple threads that need to access the object at the same time enter the waiting pool of the object to form a queue, wait for the previous thread to use it, and then use it again for the next thread

Thread synchronization (queue + lock)

Thread synchronization:

Because multiple threads of the same process share a piece of storage space, it brings convenience and access conflict. In order to ensure the correctness of data being accessed in the method, the lock mechanism synchronization is added during access. When one thread obtains the exclusive lock of the object and monopolizes resources, other threads must wait and release the lock after use, but there are the following problems

All other threads that need to hold this lock will cause all other objects to hang

In multi-threaded competition, locking and releasing locks will lead to more context switching and scheduling delays, resulting in performance problems

If a high priority thread waits for a low priority thread to release the lock, it will lead to priority inversion and performance problems

Thread unsafe cases

1. Ticket buying: there will be oversold, and there may be negative tickets

package syn;

//Unsafe ticket buying
public class UnsafeByTicket {
    public static void main(String[] args) {
        ByTicket station = new ByTicket();
        new Thread(station,"I").start();
        new Thread(station,"you").start();
        new Thread(station,"cattle").start();

    }
}

class ByTicket implements Runnable {

    //ticket
    private int ticketNum = 10;
    boolean flag = true; //External stop

    @Override
    public void run() {
//        Buy a ticket
        while (flag) {
            buy();
        }
    }

    private void buy() {
//Judge whether there are tickets
        if (ticketNum <= 0) {
            flag = false;
            return;
        }

//        Analog delay
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

//        Buy a ticket
        System.out.println(Thread.currentThread().getName() + "Get" + ticketNum--);
    }
}

//Operation results
//        I got 10
//        Scalpers get 9
//        You got eight
//        I got seven
//        Scalpers get 7
//        You got six
//        You get 4
//        Scalpers get 5
//        I got five
//        I got three
//        Cattle get 3
//        You got three
//        Cattle get 2
//        I got two
//        You got 2
//        You get 1
//        I got - 1
//        Cattle get 0

Case 2: withdraw money from the account, and the money withdrawn is greater than the deposit

package syn;

//Unsafe withdrawal
public class UnsafeBank {
    public static void main(String[] args) {
        //account
        Account account = new Account(100, "fund" );
        Drawing you = new Drawing(account, 50, "you");
        Drawing griFriend = new Drawing(account, 100, "griFriend");
        you.start();
        griFriend.start();

    }
}

//account
class Account {
    int money; //balance
    String name; //full name

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

//Bank: simulated withdrawal
class Drawing extends Thread {
    Account account; //account
    //How much money did you withdraw
    int drawingMoney;
    //s balance in hand
    int nowMoney;

    public Drawing(Account account, int drawingMoney, String name) {
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }
    //Withdraw money
    @Override
    public void run() {
//        Judge whether there is money
        if (account.money - drawingMoney <= 0) {
            System.out.println(Thread.currentThread().getName() + "take,The money is not enough");
        }
//The occurrence of sleep amplification problem
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //Card balance = = balance - get money
        account.money = account.money - drawingMoney;
        //Money in hand
        nowMoney = nowMoney + drawingMoney;
        System.out.println(account.name + "The balance is: " + account.money);
//        Thread.currentThread().getName() = this.getName()
        System.out.println(this.getName() + "Money in hand" + nowMoney);
    }
}

//        griFriend, there's not enough money
//        Fund balance: 0
//        Fund balance: - 50
//        griFriend's money 100
//        The money in your hand is 50

Case 3: unsafe collection

package syn;

import java.util.ArrayList;
import java.util.List;

//Thread unsafe collection
public class UnsafeList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}
//Theoretically, there are 10000 pieces of data, and the actual output is 9998

Synchronization method:

The private keyword ensures that data objects can only be accessed by methods, so we only need to propose a set of mechanism for methods, that is, synchronized keyword, which has two uses: synchronized method and synchronized block

Synchronization method: public synchronized void method(int args) {}

The synchronize method controls the access to the "object". Each object corresponds to a lock. Each synchronize method must obtain the lock of the object calling the method before it can be executed. Otherwise, the thread will block. Once the method is executed, it will exclusively change the lock until the method returns, and the lock will not be released until the method returns. The blocked thread can obtain the lock and continue to execute

Defects: synchronizing will affect efficiency, lock too many, and waste resources

Synchronization block: synchronized (Obj) {}

Obj is called a synchronization monitor,

  1. Obj can be any object, but it is recommended to use shared resources as synchronization monitors
  2. There is no need to specify a synchronization monitor in the synchronization method, because the synchronization monitor of the synchronization method is this, the object itself, or class

Synchronization monitor execution process

  1. The first thread accesses, locks the synchronization monitor, and executes the code in it
  2. The second thread accesses and finds that the synchronization monitor is locked and cannot be accessed
  3. After the first program is accessed, unlock the synchronization monitor
  4. The second thread accesses, finds that the synchronization monitor has no lock, and then locks the access

Test and buy tickets

package syn;

//Unsafe ticket buying
public class UnsafeByTicket {
    public static void main(String[] args) {
        ByTicket station = new ByTicket();
        new Thread(station,"I").start();
        new Thread(station,"you").start();
        new Thread(station,"cattle").start();

    }
}

class ByTicket implements Runnable {

    //ticket
    private int ticketNum = 10;
    boolean flag = true; //External stop

    @Override
    public void run() {
//        Buy a ticket
        while (flag) {
            try {
                buy();
                //        Analog delay
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

//    In addition, synchronized becomes a synchronization method, and the lock is this
    private synchronized void buy() throws InterruptedException {
//Judge whether there are tickets
        if (ticketNum <= 0) {
            flag = false;
            return;
        }

//        Buy a ticket
        System.out.println(Thread.currentThread().getName() + "Get" + ticketNum--);
    }
}

//Operation results
//        I got 10
//        Scalpers get 9
//        You got eight
//        I got seven
//        Cattle get 6
//        You got five
//        Scalpers get 4
//        You got three
//        I got two
//        You get 1

Bank case:

package com.hx.vipmodel.controller;

//Unsafe withdrawal
public class UnsafeBank {
    public static void main(String[] args) {
        //account
        Account account = new Account(100, "fund" );
        Drawing you = new Drawing(account, 50, "you");
        Drawing griFriend = new Drawing(account, 100, "griFriend");
        you.start();
        griFriend.start();

    }
}

//account
class Account {
    int money; //balance
    String name; //full name

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

//Bank: simulated withdrawal
class Drawing extends Thread {
    Account account; //account
    //How much money did you withdraw
    int drawingMoney;
    //s balance in hand
    int nowMoney;

    public Drawing(Account account, int drawingMoney, String name) {
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }
    //Withdraw money
//    Suncronized locks this by default
    @Override
    public void run() {

//        The object to be locked is the amount of change and the object to be operated (bank locked, unable to be locked)
        synchronized (account) {
//        Judge whether there is money
            if (account.money - drawingMoney <= 0) {
                System.out.println(Thread.currentThread().getName() + "take,The money is not enough");
                return;
            }
//The occurrence of sleep amplification problem
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //Card balance = = balance - get money
            account.money = account.money - drawingMoney;
            //Money in hand
            nowMoney = nowMoney + drawingMoney;
            System.out.println(account.name + "The balance is: " + account.money);
//        Thread.currentThread().getName() = this.getName()
            System.out.println(this.getName() + "Money in hand" + nowMoney);
        }

    }
}

//    output
//    Fund balance: 50
//    The money in your hand is 50
//    griFriend, there's not enough money

JUC--CopyOnWriteArrayList

package com.hx.vipmodel.controller;

import java.util.concurrent.CopyOnWriteArrayList;

//Test JUC security type collection
public class TestJUC {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

//The expected size is 1000 and the actual output is 10000

Deadlock:

In the scenario where multiple threads occupy some shared resources and wait for the resources occupied by other threads to run, resulting in two or more threads waiting for each other to release resources and stop execution, the problem of "deadlock" may occur when a synchronization block has "locks of more than two objects" at the same time.

Deadlock solution:

Four necessary conditions for deadlock generation:

  1. Mutually exclusive condition: a resource can only be used by one process at a time
  2. Request and hold condition: when a process is blocked by requesting resources, it will hold on to the obtained resources
  3. Conditions of non deprivation: the resources obtained by the process cannot be forcibly deprived until they are used
  4. Circular waiting condition: a circular waiting resource relationship between several processes

Breaking any one or more of the above four conditions will avoid deadlock

package com.hx.vipmodel.controller;

//Deadlock: multiple threads will hold the resources needed by each other, and the schedule is deadlocked
public class DeadLok {
    public static void main(String[] args) {
        Makeup m1 = new Makeup(0, "Cinderella");
        Makeup m2 = new Makeup(1, "Snow white");

        m1.start();
        m2.start();

    }
}

//Lipstick
class Lipstick{
}

//mirror
class Mirror {
}

class Makeup extends Thread {

//    Only one resource is needed, and only one is guaranteed by static
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choice; //choice
    String girName; //People who use cosmetics

    Makeup(int choice, String girName) {
        this.choice = choice;
        this.girName = girName;
    }

    @Override
    public void run() {
//        Make up
        try {
            makeip();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //    Make up, hold each other's resources, but need to get each other's resources
    private void makeip() throws InterruptedException {
        if (choice == 0) {
            synchronized (lipstick) {//Get lipstick lock
                System.out.println(this.girName + "Get lipstick lock");
                Thread.sleep(1000);
                synchronized (mirror) { //Want to get a mirror in a second
                    System.out.println(this.girName + "Get the lock of the mirror");
                }
            }
        } else {
            synchronized (mirror) {//Get the lock of the mirror
                System.out.println(this.girName + "Get the lock of the mirror");
                Thread.sleep(2000);
                synchronized (lipstick) {//Want to get lipstick in a second
                    System.out.println(this.girName+"Get the lock of the mirror");
                }
            }
        }
    }
}
//output
//Cinderella gets the lock of lipstick
//Snow White gets the lock of the mirror
//m1 gets the lipstick first, m2 gets the mirror, holds each other's desired resources, and deadlocks each other. The program will not end


    //    Each other's resources, but they need each other's resources
//    private void makeip() throws InterruptedException {
//        if (choice == 0) {
//            synchronized (lipstick) {/ / get the lock of lipstick
//                System.out.println(this.girName + "lock to get lipstick");
//                Thread.sleep(1000);
//                }
//            synchronized (mirror) {/ / want to get the mirror in one second
//                System.out.println(this.girName + "get the lock of the mirror");
//            }
//        } else {
//            synchronized (mirror) {/ / get the lock of the mirror
//                System.out.println(this.girName + "get the lock of the mirror");
//                Thread.sleep(2000);
//                }
//            synchronized (lipstick) {/ / want to get lipstick in one second
//                System.out.println(this.girName + "get the lock of the mirror");
//
//            }
//        }
//    }
//m1 gets lipstick first and m2 gets the mirror. After modification, the other party is not allowed to hold the other party's desired resources. The deadlock problem is solved after synchronized is raised

//output
//    Snow White gets the lock of the mirror
//    Cinderella gets the lock of lipstick
//    Cinderella gets the lock of the mirror
//    Snow White gets the lock of the mirror

Lock lock:

Starting from JDK 5.0, Java provides a more powerful thread synchronization mechanism - synchronization is achieved by explicitly defining synchronization Lock objects. Synchronous locks use Lock objects as

java.util.concurrent.locks.Lock interface is a tool that controls multiple threads to access shared resources. Lock provides exclusive access to shared resources. Only one thread can lock the lock object at a time. Threads should obtain the lock object before accessing shared resources

ReentrantLock (reentrant Lock) class implements Lock. It has the same concurrency and memory semantics as synchronized. ReentrantLock is commonly used in thread safety control, which can explicitly add and release locks.  

Comparison between synchronize and lock

Lock is an explicit lock (manually open and close the lock, don't forget to close the lock). synchronized is an implicit lock, which is automatically released out of the scope

Lock only has code block lock, and synchronized has code block lock and time lock

Using Lock lock, the JVM will spend less time to schedule threads, with better performance and better scalability (providing more subclasses)

Priority of use

  • Lock > sync code block (has entered the method body and allocated corresponding resources) > sync method > (outside the method body)

Test lock lock

package com.hx.vipmodel.controller;

import java.util.concurrent.locks.ReentrantLock;

//Test lock lock
public class TestLock {
    public static void main(String[] args) {

        TestLock2 testLock2 = new TestLock2();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
    }
}

class TestLock2 implements Runnable {
    int ticketNum = 10;

    //Define lock lock
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                lock.lock(); //Lock

                if (ticketNum > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(ticketNum--);
                } else {
                    break;
                }
            }finally {
//              Unlock
                lock.unlock();
            }
        }
    }
}
//output
//10
//9
//8
//7
//6
//5
//4
//3
//2
//1

Thread collaboration:

Application scenario: producer, consumer

  • Assuming that only one top-grade product can be stored in the warehouse, the producer will put the produced products into the warehouse, and the consumer will take away the products in the warehouse for consumption,
  • If there are no goods in the warehouse, the producer will put the products into the warehouse, otherwise stop production and wait until the products in the warehouse are taken away by consumers
  • If there are products in the warehouse, the consumer can take the products away for consumption, otherwise stop consumption and wait until the goods are put in the warehouse again

This is a thread synchronization problem. Producers and consumers share the same resource, and producers and consumers are interdependent and conditional on each other

  • For producers, they should inform consumers to wait before producing products, and inform consumers to consume immediately after producing products
  • For consumers, after consumption, they should inform producers that they have finished consumption and need to produce new products for consumption
  • In the problem of production and consumption, synchronized is not enough
  • synchronized can prevent concurrent updating of the same shared resource and realize synchronization
  • synchronized cannot be used for thread passing (Communication) between different threads

java provides several methods to solve the communication problem between threads

Wait() means that the thread waits until other threads notify it. Unlike sleep, wait() releases the lock

wait(long timeout) specifies the number of milliseconds to wait

notify() wakes up a waiting thread

notifyAll() wakes up all threads calling the wait() method on the same object, and the threads with higher priority are scheduled first

Note: any method of Object class can only be in the synchronization method or synchronization code block, otherwise an exception will be thrown

Solution 1: pipe pass method

Concurrent collaboration mode, "producer / consumer mode" -- > management process method

  • Producer: the module responsible for production data (may be method, object, thread, process)
  • Consumer: the module responsible for processing data (possibly method, object, thread and process)
  • Buffer: consumers cannot directly use the data of producers. There is a buffer between them

The producer puts the produced data into the buffer, and the consumer takes the data from the buffer

 

package syn;
//Test: producer consumer model -- > using buffer solution: pipe process method

import java.nio.channels.Channel;

//Producer, consumer, product, buffer
public class TestTcp {
    public static void main(String[] args) {
        SynContainer synContainer = new SynContainer();
        new Produtor(synContainer).start();
        new Consumer(synContainer).start();
    }
}

//producer
class Produtor extends Thread {
    SynContainer container;
    public Produtor(SynContainer container) {
        this.container = container;
    }
//production
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            container.push(new Chicken(i));
            System.out.println("Produced" + i + "Chicken");
        }
    }
}

//consumer
class Consumer extends Thread {
    SynContainer container;
    public Consumer(SynContainer container) {
        this.container = container;
    }

//    x consumer
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("Consumption"+container.pop().id+"Chicken");
        }
    }
}

///Products
class Chicken {
    int id; //number
    public Chicken(int id) {
        this.id = id;
    }
}

//buffer
class SynContainer {
    //Container size
    Chicken[] chickens = new Chicken[10];
//Counting container
    int count = 0;
    //    Producers put in products
    public synchronized void push(Chicken chicken) {
        //If the container is full, consumers need to consume it
        if (count == chickens.length) {
        //Production waiting
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
//        If it is not full, you need to throw in the product
        chickens[count] = chicken;
        count++;
        //Consumers can be informed of consumption
        this.notifyAll();
    }

//Consumer products
    public synchronized Chicken pop() {
//Judge whether to consume
        if (count == 0) {
            //Wait for producers to produce and consumers to wait
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //If you can consume
        count--;
        Chicken chicken = chickens[count];
        //Inform the producer of production after consumption
        this.notifyAll();
        return chicken;
    }
}

Solution 2:

Concurrent collaboration model: "producer / consumer" -- > signal lamp method

Judge a flag bit. If it is true, let it wait. If it is equal to false, let it notify another person. Like a traffic light, "red light stops, green light goes" to judge when the thread listens and wakes up

package syn;

//Test producer consumer problem 2: signal lamp method, flag bit solution
public class TestPc2 {
    public static void main(String[] args) {
        Tv tv = new Tv();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}

//Producer -- > actor
class Player extends Thread {
    Tv tv;
    public Player(Tv tv) {
        this.tv = tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i % 2 == 0) {
                this.tv.play("Playing:A dog's way home");
            } else {
                this.tv.play("Playing:US troops withdraw from Afghanistan");
            }
        }
    }
}

//Consumer -- > audience
class Watcher extends Thread {
    Tv tv;
    public Watcher(Tv tv) {
        this.tv = tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}

//Products -- > Programs
class Tv {
    //The actors performed and the audience waited
    // The audience watched and the actors waited
    String voice; //A performance
    boolean flag = true; //t actors perform, f actors wait
    //perform
    public synchronized void play(String voice) {
        if (!flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("The actors performed:" + voice);
        //After the performance, inform the audience to watch
        this.notifyAll();//Notification wake-up
        this.voice = voice;
        this.flag = !this.flag;
    }
    //    watch
    public synchronized void watch() {
        if (flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("Watched:" + voice);
//        Inform the actors to perform
        this.notifyAll();
        this.flag = !this.flag;
    }
}

Thread pool:

Background: resources that are often created and destroyed and used heavily, such as threads in concurrency, have a great impact on performance.

Idea: create many threads in advance, put them into the thread pool, get them directly when using them, and put them back into the pool after using them. It can avoid frequent creation, destruction and reuse. Similar vehicles in public life.


Benefits:
Improved response speed (reduced time to create new threads)
Reduce resource consumption (reuse threads in the thread pool, not creating them every time)

Easy thread management

  • corePoolSize: the size of the core pool
  • maximumPoolSize: maximum number of threads
  • keepAliveTime: when the thread has no task, how long will it last at most and then terminate
     

JDK 5.0 provides thread pool related APIs: executorservice and Executors

ExecutorService: the real thread pool interface. Common subclass ThreadPoolExecutor

  • void execute(Runnable command): executes a task / command without a return value. It is generally used to execute Runnable
  • <T> Future < T > submit (Callable < T > task): when a task is executed, there is a return value. Generally, Callable is executed again
  • void shutdown(): closes the connection pool

Executors: tool class and factory class of thread pool, which are used to create and return different types of thread pools

package syn;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

//Test thread pool
public class TestPool {
    public static void main(String[] args) {
//        1. Create service and thread pool
//        2. Set the thread pool size newFixedThreadPool
        ExecutorService service = Executors.newFixedThreadPool(10);
//        implement
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
//        Close connection
        service.shutdown();
    }
}

class MyThread implements Runnable {

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

Ways to implement multithreading:

package syn;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

//Review the creation of all thread pools
public class AllThread {
    public static void main(String[] args) {
        new MyThread1().start();
        new Thread(new MyThread2()).start();
        FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());
        new Thread(futureTask).start();
        try {
            Integer integer = futureTask.get();
            System.err.println(integer);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

//1. Inherit Thread
class MyThread1 extends Thread {
    @Override
    public void run() {
        System.out.println("extends Thread ");
    }
}

//Implement Runnable interface
class MyThread2 implements Runnable {
    @Override
    public void run() {
        System.out.println("implements Runnable");
    }
}

//Implement Callable interface
class MyThread3 implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("implements Callable");
        return 100;
    }
}



 

Keywords: Java Back-end

Added by Monadoxin on Thu, 03 Mar 2022 18:07:43 +0200