Concurrent container and framework for Java concurrent programming

ConcurrentHashMap

ConcurrentHashMap is a thread-safe and efficient HashMap.

Principle: divide the data into segments and store it in segments, and then assign a lock to each segment of data. When a thread occupies the lock to access one segment of data, the data in other segments can also be accessed by other threads.

ConcurrentLinkedQueue

It is an unbounded thread-safe queue based on linked nodes, which uses a first-in first-out rule

Then the nodes are sorted. When we add an element, it will be added to the end of the queue; when we get an element, it will return the element at the head of the queue. It uses the "wait-free" algorithm (CAS algorithm) to achieve.

Blocking queue

A queue that supports two additional operations. These two additional operations support blocking insertion and removal methods.

1) Support blocking insertion method: This means that when the queue is full, the queue will block the thread inserting the element until the queue is not full.

2) Support blocking removal method: This means that when the queue is empty, the thread that gets the element will wait for the queue to become non-empty.

Blocking queues are often used in producer and consumer scenarios. Producers are threads that add elements to the queue, and consumers are threads that take elements from the queue. A blocking queue is a container used by producers to store elements and consumers to obtain elements.

  1. ArrayBlockingQueue

This queue sorts the elements according to the first in first out (FIFO) principle.

  1. LinkedBlockingQueue

A bounded blocking queue implemented with a linked list.

  1. PriorityBlockingQueue

An unbounded blocking queue that supports priority.

  1. DelayQueue

An unbounded blocking queue that supports delayed acquisition of elements. The queue is implemented using PriorityQueue. The elements in the queue must implement the Delayed interface. When creating the element, you can specify how long it takes to get the current element from the queue. Only when the delay expires can elements be extracted from the queue.

  1. SynchronousQueue

A blocking queue that does not store elements. Each put operation must wait for a take operation, otherwise it cannot continue to add elements.

  1. LinkedTransferQueue

An unbounded blocking TransferQueue queue composed of a linked list structure. Compared with other blocking queues, LinkedTransferQueue has more tryTransfer and transfer methods.

  1. LinkedBlockingDeque

A two-way blocking queue composed of a linked list structure. The so-called two-way queue refers to

Insert and remove elements from both ends of the queue.

Implementation of blocking queue, ArrayBlockingQueue :

Use notification mode to achieve.

//不为空的通知

private final Condition notEmpty;

//不为full(队列塞满)的通知

private final Condition notFull;

    public ArrayBlockingQueue(int capacity, boolean fair) {

        notEmpty = lock.newCondition();

        notFull =  lock.newCondition();

}

//阻塞方式插入队列

    public void put(E e) throws InterruptedException {

        checkNotNull(e);

        final ReentrantLock lock = this.lock;

        lock.lockInterruptibly();

        try {

//当队列满员时,将等待入队,等待消费item

            while (count == items.length)

                notFull.await();

            enqueue(e);

        } finally {

            lock.unlock();

        }

}

    private void enqueue(E x) {

        final Object[] items = this.items;

        items[putIndex] = x;

        if (++putIndex == items.length)

            putIndex = 0;

        count++;

//当队列中插入item后,通知消费者继续获取item进行消费

        notEmpty.signal();

    }

//阻塞方式获取元素

    public E take() throws InterruptedException {

        final ReentrantLock lock = this.lock;

        lock.lockInterruptibly();

        try {

//当队列中没有元素时,等待生产者插入item元素

            while (count == 0)

                notEmpty.await();

            return dequeue();

        } finally {

            lock.unlock();

        }

    }

    private E dequeue() {

        final Object[] items = this.items;

        @SuppressWarnings("unchecked")

        E x = (E) items[takeIndex];

        items[takeIndex] = null;

        if (++takeIndex == items.length)

            takeIndex = 0;

        count--;

        if (itrs != null)

            itrs.elementDequeued();

//当队列中item出队时,通知生产者队列有空闲位置,可以继续插入元素

        notFull.signal();

        return x;

    }

Through the source code, it can be seen that ArrayBlockQueue is a concurrent container controlled by the Lock lock, and the code is very simple.

Fork/Join framework

A framework for parallel execution of tasks is a framework that divides a large task into several small tasks, and finally summarizes the results of each small task to get the result of the large task.

The work-stealing algorithm refers to a thread stealing tasks from other queues for execution.

In order to reduce the competition between threads, these subtasks are placed in different queues, and a separate thread is created for each queue to execute the tasks in the queue. The threads and the queues correspond one to one. For example, the A thread is responsible for processing tasks in the A queue. However, some threads will finish the tasks in their queues first, while there are still tasks waiting to be processed in the queues corresponding to other threads. Instead of waiting, the thread that finishes its work might as well help other threads to work, so it goes to the queue of other threads to steal a task for execution. At this time, they will access the same queue, so in order to reduce the theft of task threads and be

The competition between the stealing task threads usually uses a deque. The stolen task thread will always execute the task from the head of the deque, and the thread stealing the task will always execute the task from the tail of the deque.

 

Work-stealing algorithm advantages point : full use of the line path into rows parallel count calculation, reducing the wire path between the competition contention.

Shortcoming work-stealing algorithm: in some cases also exist competition dispute, such as double-ended team row only one of any task when . And the algorithm will consume more system system resource sources, such as creating build multiple line drive double-ended and multiple teams columns.

Fork/Join  steps

Step 1 Split the task.

Step 2 Execute the task and merge the results.

Fork/Join execution

①ForkJoinTask task creation

RecursiveAction: Used for tasks that do not return results.

RecursiveTask: Used for tasks that return results.

②ForkJoinPool: ForkJoinTask needs to be executed through ForkJoinPool.

 

ForkJoinTask provides the isCompletedAbnormally() method to check whether the task has thrown an exception or has been cancelled, and the exception can be obtained through the getException method of ForkJoinTask.

if(task.isCompletedAbnormally()){

System.out.println(task.getException());

}

The getException method returns a Throwable object, if the task is cancelled, it returns a CancellationException. If the task is not completed or no exception is thrown, null is returned.

ForkJoinPool is composed of ForkJoinTask array and ForkJoinWorkerThread array. ForkJoinTask array is responsible for submitting the storage program to ForkJoinPool tasks, and ForkJoinWorkerThread array is responsible for performing these tasks

 

There are 4 task states: completed (NORMAL), cancelled (CANCELLED), signal (SIGNAL) and abnormal (EXCEPTIONAL).

Forkjoin two task allocation methods

RecursiveTask :

public class CountTask extends RecursiveTask<Integer> {

    private static final int THRESHOLD = 2; // 阈值
    private int              start;
    private int              end;

    public CountTask(int start, int end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Integer compute() {
        int sum = 0;

        // 如果任务足够小就计算任务
        boolean canCompute = (end - start) <= THRESHOLD;
        if (canCompute) {
            for (int i = start; i <= end; i++) {
                sum += i;
            }
        } else {
            // 如果任务大于阈值,就分裂成两个子任务计算
            int middle = (start + end) / 2;
            CountTask leftTask = new CountTask(start, middle);
            CountTask rightTask = new CountTask(middle + 1, end);
            //执行子任务
            leftTask.fork();
            rightTask.fork();
            //等待子任务执行完,并得到其结果
            int leftResult = leftTask.join();
            int rightResult = rightTask.join();
            //合并子任务
            sum = leftResult + rightResult;
        }
        return sum;
    }

    public static void main(String[] args) {
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        // 生成一个计算任务,负责计算1+2+3+4
        CountTask task = new CountTask(1, 100);
        // 执行一个任务
        Future<Integer> result = forkJoinPool.submit(task);
        try {
            System.out.println(result.get());
        } catch (InterruptedException e) {
        } catch (ExecutionException e) {
        }
    }

}

RecursiveAction :

public class CountAction extends RecursiveAction {

	private static final int THRESHOLD = 2; // 阈值
	private int start;
	private int end;
	private static AtomicInteger atomicInteger=new AtomicInteger();

	public CountAction(int start, int end) {
		this.start = start;
		this.end = end;
	}
	@Override
	protected void compute() {
		int sum = 0;
		// 如果任务足够小就计算任务
		boolean canCompute = (end - start) <= THRESHOLD;
		if (canCompute) {
			for (int i = start; i <= end; i++) {
				sum += i;
			}
			atomicInteger.addAndGet(sum);
		} else {
			// 如果任务大于阈值,就分裂成两个子任务计算
			int middle = (start + end) / 2;
			CountAction leftAction = new CountAction(start, middle);
			CountAction rightAction = new CountAction(middle + 1, end);
			// 执行子任务
			leftAction.fork();
			rightAction.fork();
		}
	}

	public static void main(String[] args) {
		ForkJoinPool forkJoinPool = new ForkJoinPool();
		// 生成一个计算任务,负责计算1+2+3+4
		CountAction action = new CountAction(1, 100);
		// 执行一个任务
		forkJoinPool.execute(action);
		forkJoinPool.awaitQuiescence(50, TimeUnit.HOURS);
		System.out.println(action.atomicInteger);
	}

}

 

Guess you like

Origin blog.csdn.net/weixin_44416039/article/details/86163402