[JUC Learning Notes] - [Basic Article 5]

1. Read-write lock ReentrantReadWriteLock

Concepts:
  • Read-write locks are designed to solve the problem of multiple threads writing simultaneously and having multiple threads reading at the same time, causing threads to read data before it has been written. A read-write lock is a lock that allows multiple threads to read at the same time, but allows only one thread to write, thereby ensuring data security.
Degradation of read and write locks:
  • Why is demotion, because the permissions of write operations are higher than those of read operations, that is, the process from high to low is called demotion. Read and write locks are not allowed to write while reading, but are allowed to read when writing. The process is to acquire a write lock before acquiring a read lock --> release a write lock --> release a read lock.
public class ReentrantReadWriteLockDemo {
    public static void main(String[] args) {
        ReentrantReadWriteLock reentrantLock = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.WriteLock  writeLock =  reentrantLock.writeLock();
        ReentrantReadWriteLock.ReadLock readLock = reentrantLock.readLock();

        //Get Write Lock First
        writeLock.lock();
        System.out.println("Get Write Lock First");

        //Get Read Lock Again
        readLock.lock();
        System.out.println("Get Read Lock Again");

        //Release Write Lock
        writeLock.unlock();
        //Release Read Lock
        readLock.unlock();
    }
}

Can we upgrade the lock?

  		//Get Read Lock First
        readLock.lock();
        System.out.println("Get Read Lock First");

        //Get a write lock again
        writeLock.lock();
        System.out.println("Get a write lock again");

It seems unreasonable that my program has stopped here and I have a new thread writing to it while I am reading. I have read the lock and then I can write.

2. Blocking Queue

Concepts:
  • The characteristics of queues: First in, first out, first in, first out of the queue. A blocked queue is one in which a full queue is blocked when a thread puts data from the left to the queue, or when a thread fetches data from the right of the queue, an empty queue is blocked. This is called a blocked queue.
Classification:
Namebrief introduction
ArrayBlockingQueueBased on Array, Fixed Length, Maximum Length Integer.MAX_VALUE
LinkedBlockingQueueBased on chain list, fixed length, maximum length Integer.MAX_VALUE
DelayQueueSet a delay time, the delay time can be reached before removal, unbounded blocking queue, producer will not block but consumer will
PriorityBlockingQueueSort using Comparator Comparator, unbounded blocking queue
SynchronousQueueOnly one element is allowed in the queue, and each insert operation must wait for the deletion of another thread
LinkedTransferQueueUnbounded Queue Based on Chain List
LinkedBlockingDequeAn optional bounded blocking double-ended queue based on the link node. Optional capacity-bound constructor parameters are used as a way to prevent overexpansion. Capacity (if not specified) equals Integer.MAX_VALUE. Link nodes are created dynamically each time they are inserted, unless this causes a double-ended queue to exceed capacity.
Core approach:
classificationThrow an exceptionBoolean and nullblockovertime
Newly addedadd(e)offer(e)put(e)offer(e,time,unit)
deleteremove()poll(), empty queue returns nulltake()poll(time,unit)
inspectelement()peek()

3. ThreadPool

Concepts:
  • The pool used to hold threads, where one thread is used and then put back to the pool to achieve recycling efficiency, but creating too many threads incurs additional overhead and may not improve program efficiency or reduce efficiency.
The Executors Thread Tool class creates a thread pool:
  • newFixedThreadPool creates a specified number of thread pools

    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
    
  • newSingleThreadExecutor creates a single thread pool

    ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
    
  • newCachedThreadPool has a flexible thread pool for creating threads, multiple tasks for creating threads, and the number of threads previously set is restored when the task is finished

    ExecutorService executorService = Executors.newCachedThreadPool();
    
Custom thread pool:
  • When creating a thread pool through the Executors tool class, the underlying layer is created through the new ThreadPoolExecutor() object

  • Seven parameters for ThreadPoolExecutor

    Parameter NameInterpretation
    int corePoolSizeNumber of Core Threads
    int maximumPoolSizeMaximum Threads
    long keepAliveTimeTime to stay alive
    TimeUnit unitUnit of time
    BlockingQueue workQueueBlocking Queue
    ThreadFactory threadFactoryCreate Thread Factory for Threads
    RejectedExecutionHandler handlerDenial policy with a full queue
Thread pool execution process:

In practice, we don't use the default three ways to create thread pools, because they are all fast enough to cause OOM, more custom thread pools.

4. Fork and Join

Concepts:
  • Fork: Split a large task into smaller tasks
  • Join: Combine small task results into one result
Use:

Complete the sum of 0-100: requires the difference between two numbers to be less than or equal to 10

First create a task to inherit RecursiveTask

class MyTask extends RecursiveTask<Integer> {
    private static final Integer NUMBER = 10;
    private  int begin;
    private  int end;
    private  int result;

    public MyTask(int begin,int end) {
        this.begin = begin;
        this.end = end;
    }

    @Override
    protected Integer compute() {
        if ((end-begin) <= NUMBER){
            for (int i = begin; i <= end ; i++) {
                result += i;
            }
        }else {
            int middle = (begin + end) >> 1;

            MyTask beginToMiddle = new MyTask(begin, middle);

            MyTask middleToEnd = new MyTask(middle + 1, end);
            //Split result
            beginToMiddle.fork();
            middleToEnd.fork();

            //Merge results
            result = beginToMiddle.join() + middleToEnd.join();
        }
        return result;
    }
}

Use forkJoinPool to complete tasks

public class ForkAndJoin {

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        ForkJoinPool forkJoinPool = new ForkJoinPool();
        MyTask myTask = new MyTask(0,100);

        ForkJoinTask<Integer> forkJoinTask = forkJoinPool.submit(myTask);
        Integer value = forkJoinTask.get();
        System.out.println(value);
        
        forkJoinPool.shutdown();
    }
}

Tags: Java JUC

Posted by scotthoff on Sat, 16 Jul 2022 22:55:53 +0530