Java learning notes day23 - multithreading advanced

Posted by rupam_jaiswal on Thu, 24 Feb 2022 19:00:56 +0100

Multithreading advanced

Six states of threads in virtual machine

  • New status (NEW) -- create thread object
  • Ready state (runnable) -- start method
  • Blocked ------------- unable to get lock object
  • Waiting -- waiting method
  • Timed_waiting -- sleep method
  • Terminated -------------- all codes have been run

Thread pool

code implementation
  • Create a pool that is empty ------------- create a static method in Executors

  • Create a thread object when a task needs to be executed; After the task is executed, the thread object is returned to the pool ------------- submit method

    (the pool will automatically create objects for us. After the task is completed, the thread object will also be returned to the pool automatically)

  • After all tasks are completed, close the connection pool ------------- shutdown method

Create default thread pool

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

public class MyThreadPoolTest {
    public static void main(String[] args) throws InterruptedException {
        //Create a pool. The pool is empty. By default, it can accommodate the maximum value of int type
        ExecutorService executorService = Executors.newCachedThreadPool();
        //Executors ---- can help us create thread pool objects
        //ExecutorService ---- can help us control the thread pool
        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName()+"In execution");
        });
        //Thread.sleep(2000);
        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName()+"In execution");
        });
        executorService.shutdown();
    }
}

Creates a thread pool with a specified upper limit

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

public class MyThreadPoolTest2 {
    public static void main(String[] args) {
        //The parameter is not the initial value but the maximum value
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName()+"In execution");
        });
        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName()+"In execution");
        });
        executorService.shutdown();
    }
}
Create thread pool manually
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MyThreadPoolTest3 {
    public static void main(String[] args) {
        //Parameter 1: number of core threads -- cannot be less than 0
        //Parameter 2: maximum number of threads ---- cannot be less than or equal to 0, maximum number > = number of core threads
        //Parameter 3: maximum idle thread lifetime -- cannot be less than 0
        //Parameter 4: time unit - TimeUnit
        //Parameter 5: task queue - cannot be null. Let the task wait in the queue. When a thread is idle, get the task from the queue and execute it
        //Parameter 6: create thread factory - cannot be null. Create thread object by default
        //Parameter 7: task rejection policy - cannot be null. When the submitted task > the maximum number of threads in the pool + queue capacity, the task is rejected;
        ThreadPoolExecutor pool = new ThreadPoolExecutor(2,5,2, TimeUnit.SECONDS,new ArrayBlockingQueue<>(10), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
        pool.submit(()->{
            System.out.println(Thread.currentThread().getName()+"In execution");
        });
        pool.submit(()->{
            System.out.println(Thread.currentThread().getName()+"In execution");
        });
        pool.shutdown();
    }
}
Task rejection policy

ThreadPoolExecutor.AbortPolicy: discard the task and throw RejectedExecutionException exception; Default policy

ThreadPoolExecutor.DiscardPolicy: discard the task without throwing an exception, which is not recommended;

ThreadPoolExecutor. Discard oldest policy: discard the task that has been waiting for the longest time in the queue, and then add the current task to the queue;

ThreadPoolExecutor.CallerRunsPolicy: call the run() method of the task to bypass the thread pool and execute directly

Volatile
  1. Heap memory is unique, and each thread has its own thread stack;
  2. When each thread uses the variables in the heap, it will first copy a copy to the variable copy;
  3. In the thread, each use is obtained from the copy of the variable;

Question:

If thread A modifies the variables shared in the heap, other threads may not be able to use the latest values in time.

Volatile keyword, which forces the thread to look at the latest value of the shared area every time it is used.

Example:

public class LittleFish {
    public static volatile int fish = 5;
}

public class WhiteCat extends Thread {
    @Override
    public void run() {
        while (LittleFish.fish == 5){

        }
        System.out.println("The dried fish is less than 5 kg");
    }
}

public class BlackCat extends Thread {
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
            LittleFish.fish = 4;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class Test {
    public static void main(String[] args) {
        WhiteCat wc = new WhiteCat();
        wc.setName("Xiaobai");
        wc.start();
        BlackCat bc = new BlackCat();
        bc.setName("Xiao Hei");
        bc.start();
    }
}
Atomicity

The so-called atomicity means that in one operation or multiple operations, either all operations have been executed and will not be interrupted by any factor; Or all operations are not executed, and multiple operations are an inseparable whole;

volatile keyword: it can only ensure that the thread is the latest value every time it uses shared data, but atomicity cannot be guaranteed.

Atomic class AtomicInteger
  • public AtomicInteger(): initializes an atomic Integer with a default value of 0
  • public AtomicInteger(int initialValue): initializes an atomic Integer with a specified value
  • int get(): get value
  • int getAndIncrement(): atomically add 1 to the current value. Note that the value before self increment is returned here
  • int incrementAndGet(): add 1 to the current value atomically. Note that the value returned here is the value after self increment
  • int addAndGet(int data): atomically add the entered value to the value in the instance (value in AtomicInteger) and return the result
  • int getAndSet(int value): the value set atomically to newValue and returns the old value

Example:

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicTest {
    public static void main(String[] args) {
        AtomicInteger ai = new AtomicInteger();
        System.out.println(ai); //0
        AtomicInteger ai2 = new AtomicInteger(10);
        System.out.println(ai2); //10
        System.out.println(ai2.get()); //10
        int andIncrement = ai2.getAndIncrement();
        System.out.println(andIncrement); //10
        System.out.println(ai2.get()); //11
        int i = ai2.incrementAndGet();
        System.out.println(i); //12
        System.out.println(ai2.get()); //12
        int i1 = ai2.addAndGet(10);
        System.out.println(i1); //22
        System.out.println(ai2.get()); //22
        int andSet = ai2.getAndSet(10);
        System.out.println(andSet); //22
        System.out.println(ai2.get()); //10
    }
}
The difference between synchronized and CAS

The same point: in the case of multithreading, the security of shared data can be guaranteed.

Difference: synchronized always starts from the worst point of view, thinking that each time data is obtained, others may modify it. Therefore, it will be locked before each operation to share data. (pessimistic lock)

CAS is from an optimistic point of view, assuming that no one will modify the data every time it is obtained, so it will not be locked. However, when modifying shared data, you will check whether others have modified this data. If others have modified it, get the current latest value again. If no one else has modified it, you can directly modify the value of the shared data. (Le Guan lock)

Concurrent tool class

Hashtable

HashMap is thread unsafe (there may be problems in multi-threaded environment); In order to ensure the security of data, we can use Hashtable, but Hashtable is inefficient;

  • Hashtable takes the form of pessimistic lock synchronized to ensure the security of data;
  • As long as there is thread access, the whole table will be locked, so the efficiency of Hashtable is low;
ConcurrentHashMap

1.7 principle

1.8 principle

  1. If you create a ConcurrentHashMap object using null parameter construction, you don't have to do anything. Create a hash table when adding elements for the first time;
  2. Calculate the index that the current element should be stored in;
  3. If the index position is null, use CAS algorithm to add this node to the array;
  4. If the index position is not null, the volatile keyword is used to obtain the latest node address of the current position, which is hung under it and becomes a linked list;
  5. When the length of the linked list is greater than or equal to 8, it will be automatically converted into a red black tree;
  6. Take the linked list or red black tree head node as the lock object, and cooperate with the pessimistic lock to ensure the security of data when multithreading operation sets;
CountDownLatch

Usage scenario: let a thread wait for other threads to execute.

methodexplain
public CountDownLatch(int count)Parameter number of passing threads, indicating the number of waiting threads
public void await()Let the thread wait
public void countDown()The current thread has finished executing

Example:

import java.util.concurrent.CountDownLatch;

public class ChildThread1 extends Thread {
    CountDownLatch countDownLatch;
    public ChildThread1(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
        //Eat dumplings
        for (int i = 0; i < 10; i++) {
            System.out.println(getName() + "Eating the first" + i + "A dumpling");
        }
        //Say it after eating
        //Every time the countDown method is called, the counter is set to - 1
        countDownLatch.countDown();
    }
}

public class ChildThread2 extends Thread {
    CountDownLatch countDownLatch;
    public ChildThread2(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }
    @Override
    public void run() {
        //Eat dumplings
        for (int i = 0; i < 15; i++) {
            System.out.println(getName() + "Eating the first" + i + "A dumpling");
        }
        //Say it after eating
        //Every time the countDown method is called, the counter is set to - 1
        countDownLatch.countDown();
    }
}

import java.util.concurrent.CountDownLatch;

public class ChildThread3 extends Thread {
    CountDownLatch countDownLatch;
    public ChildThread3(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }
    @Override
    public void run() {
        //Eat dumplings
        for (int i = 0; i < 12; i++) {
            System.out.println(getName() + "Eating the first" + i + "A dumpling");
        }
        //Say it after eating
        //Every time the countDown method is called, the counter is set to - 1
        countDownLatch.countDown();
    }
}
import java.util.concurrent.CountDownLatch;

public class MotherThread extends Thread {
    CountDownLatch countDownLatch;
    public MotherThread(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
        //wait for
        try {
            //When the counter becomes 0, it will automatically wake up the waiting thread here
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //Clean up the dishes and chopsticks
        System.out.println("Mother is cleaning up the dishes");
    }
}

import java.util.concurrent.CountDownLatch;

public class MyCountDownLatchTest {
    public static void main(String[] args) {
        //To create the object of CountDownLatch, you need to pass it to four threads
        //A counter is defined at the bottom, and the value of the counter is 3
        CountDownLatch countDownLatch = new CountDownLatch(3);
        //Create 4 Thread objects and turn them on
        MotherThread mt = new MotherThread(countDownLatch);
        mt.start();
        ChildThread1 ct1 = new ChildThread1(countDownLatch);
        ChildThread2 ct2 = new ChildThread2(countDownLatch);
        ChildThread3 ct3 = new ChildThread3(countDownLatch);
        ct1.setName("Xiao Hong");
        ct2.setName("Xiao Ming");
        ct3.setName("Xiaolan");
        ct1.start();
        ct2.start();
        ct3.start();

    }
}
Semaphore

Usage scenario: you can control the number of threads accessing specific resources.

  1. Someone needs to manage this channel ------------- create Semaphore object

  2. When a car comes in, issue a pass

  3. Take back the pass when the car goes out

  4. If the permit is issued, other vehicles can only wait

Code implementation:

import java.util.concurrent.Semaphore;

public class MyRunnable implements Runnable {
    //Get administrator object
    private Semaphore semaphore = new Semaphore(2);
    @Override
    public void run() {
        //Get a pass
        try {
            semaphore.acquire();
            //Start exercising
            System.out.println("Get a pass and start driving");
            Thread.sleep(2000);
            System.out.println("Return the pass");
            //Return the pass
            semaphore.release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class MySemaphoreTest {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();
        for (int i = 0; i < 50; i++) {
            new Thread(mr).start();
        }
    }
}

Topics: Java