Art Java concurrent programming - reading summary

After reading "Java Concurrency in the art of" summary and induction. If you have questions, please correct me.

Chapter 1 concurrent programming challenges

1.1 context switching

1. The task will be saved before switching on the status of a task, in order to switch back to the task in the next time, you can then load the status of the task. So the task is to save a context switch from the process of reloading.

2. How to reduce context switching?

  • Lock-free concurrent programming

  • CAS algorithm

  • With a minimum thread

  • Use coroutine: multitask scheduling implemented in a single thread, and maintain the switching between a plurality of tasks in a single thread

1.2 Deadlock

1. How to avoid deadlocks?

  • Avoid a thread gets more locks

  • Avoid a thread lock again take up more resources, try to ensure that only one thread occupy a resource

  • Try using time lock, using lock.tryLock (timeout) instead of using the internal locking mechanism

  • For database locks, locking and unlocking must be a database connection, otherwise the situation will appear unsuccessful

Chapter 2 Java concurrency mechanisms underlying implementation principle

2.1 volatile applications

  1. volatile is lightweight synchronized, to ensure that he shared variables in multiple concurrent processors in the "visibility" . If a field is declared as volatile, Java thread memory model ensures that all threads see the value of this variable is the same.

  2. It does not cause context switching and scheduling.

  3. volatile realization of the principle of two

  • Lock prefix instructions cause the processor cache is written back to memory
  • A processor's cache memory is written back to causes other processor's cache is invalid

2.2 synchronized

1.synchronized basis to achieve synchronization: the Java each object can be used as a lock.

2. The three forms:

  • For normal synchronization method, the lock is the current instance of the object;
  • For static synchronization method, the lock is Class object of the current class;
  • For the synchronization code blocks, the object lock is configured Synchronized brackets.

Comparison 2.2.2 upgrade and lock

  1. No lock status - "biased locking -" lightweight lock - "heavyweight lock. Lock can only upgrade can not be downgraded.

  2. Close biased locking delay (usually biased locking is activated only after the program starts a few seconds)

-XX:BiasedLockingStartupDelay = 0;

  1. Close biased locking (the default state after entering the lightweight lock program)

-XX:UseBiasedLocking = false;

  1. Lightweight unlock the lock fails, indicating the current presence of lock contention, lock on the back of expansion for the heavyweight lock.

5. Lock advantages and disadvantages:

  • advantage
  1. Biased locking: locking and unlocking no additional consumption, there is only nanosecond gap and perform asynchronous method compared to

  2. Lightweight lock: thread does not block competition, improve the response speed of the procedure;

  3. Heavyweight locks: Thread compete without the use of spin, do not consume CPU

  • Shortcoming
  1. Biased locking: If there is a thread lock contention, will bring additional lock revocation of consumption;

  2. Lightweight lock: If still unable to get a lock competing threads, using spin will consume CPU

  3. Heavyweight lock: thread is blocked, slow response times

  • Applicable scene
  1. Biased locking: only one thread is adapted to access the sync block

  2. Lightweight lock: the pursuit of response time, synchronized block the implementation of very fast

  3. Heavyweight locks: the pursuit of certain sync blocks long execution speed

2.3 atomic operation

  1. How processor implements an atomic operation?

(1) using the bus key guarantee atomicity;

(2) using the cache lock guarantee atomicity;

Except in two cases:

  • When the operation data can not be cached within the processor, or the operation of a plurality of cache line of data across the processor bus lock calls
  • Some processors do not support cache locking
  1. How to implement Java atomic operation?

By locking and cyclic CAS implemented;

(1) using the CAS cycle operation atomicity

CAS operation of the JVM by the CMPXCHG instructions provided by the processor implementation. The basic idea is to achieve the spin cycle CAS CAS know so far successful operation.

(2) CAS operation of the Three Atomic

  1. ABA problem

    1. Solution: Use the version number, the version number is added in front of the variable, each variable is updated when the version number +1
  2. Large overhead long cycle time

  3. Atomic operation can only guarantee a shared variable

(3) using the lock mechanism to achieve

This mechanism ensures that the memory area is only a thread to acquire a lock to be able to operate the lock.

Chapter 4 Java concurrent programming foundation

4.1 Introduction Thread

4.1.1 What is the thread

1. Modern operating systems smallest unit of scheduling is the thread, also called lightweight processes . You can create a process where multiple threads, these threads have their own counter, stack, stack property, and local variables, and can access the shared memory variables.

4.1.2 Why use multithreading

  1. More processor cores
  2. Faster response time
  3. Better programming model

4.1.3 thread priority

  1. Thread for obstruction frequently (sleep or I / O operation) needs to be set a high priority, and the emphasis is calculated (CPU requires more computation time or partial) of the thread is set to lower priority to ensure that the processor is not exclusive;

  2. Thread priority is not as dependent on the correctness of the program, because the OS can ignore Java threads for priority setting.

4.1.4 thread state

4.1.5 Daemon thread

  1. A supportive type threads, primarily used as a background program scheduling and supportive work; mean, when Daemon thread does not exist a Java Virtual Machine, the Java virtual machine will exit .

  2. Thread.setDarmon (true) to set up, need to be set before starting the thread.

  3. When building Daemon thread, you can not rely on the contents of the finally block to ensure the implementation of logic resources to clean up or shut down.

4.2 initiation and termination thread

4.2.1 construction thread

4.2.2 start threads

After initialization is complete, call the start () method can start threads. Before starting a thread, the thread is best to set the thread name.

4.2.3 understand interrupted

  1. Interrupt: understood as a property of the thread flag, which indicates whether a running thread by another thread was interrupted operations. Like other thread interrupts the thread made a call, other threads of the thread by calling interrupt () method its interrupt operation.

  2. Thread () to determine whether or not interrupted, you can also call the static method Thread.interrupted () for the current thread interrupt flag reset by isInterrupted.

4.3 Inter-thread communication

4.3.2 Waiting / notification mechanism

Call wait (), notify (), notifyAll () Note:

  • We need to use to call the object lock;
  • After calling wait (), the thread state changes Running Waiting, the current thread and placed into the object queue;
  • notify, notifyAll after the call, still waiting thread does not return from wait, the calling thread releases the lock needs to notify and notifyAll after waiting for a chance to return to the thread from the wait;
  • notify, notifyAll waiting thread will move to the synchronous queue queue, is moved by the thread state changes Waiting Blocked
  • From the premise wait method returns the calling object is to obtain a lock

4.3.3 wait classic paradigm / notifications

Wait for the party:

  1. Acquiring an object lock
  2. If the condition is not met, then call the object's wait () method, after being notified still checking conditions
  3. Corresponding logical condition is satisfied is executed
  4. Pseudo code
synchronized(对象) {
    while(条件不满足) {
        对象.wait();
    }
    // 对应的逻辑处理 
}

Notify Party:

  1. To obtain the object lock
  2. Changing conditions
  3. Notify all waiting on the object thread
  4. Pseudo code
synchronized(对象) {    
    改变条件    
    对象.notifyAll(); 
} 

4.3.4 input and output flow duct

4.3.5 Thread.join () use

If a thread A executed thread.join () statement, the meaning is: A current thread after thread waiting thread to terminate () returns from thread.join. In addition to providing a thread Thread join () method is also provided a join (long mills) and join (long mills, int nanos) includes two timeout method.

4.4 threaded application examples

4.4.3 thread pool and its examples

Thread Pool: previously creating several number of threads, thread creation and can not be controlled directly by the user, using a fixed or relatively fixed repetition number of threads in this context to complete the mission . The benefit of this, on the one hand, eliminating the frequent thread creation and demise of the thread system resource overhead , on the other hand, face the task of overdose can submit gradual deterioration .

Chapter lock in 5 Java

5.1 Lock Interface

  1. And synchronized difference:
  • Implicit acquire missing releasing locks (or by the method provided by the synchronized block) convenience;
  • Have a lock acquisition and release of operability. Interruptible lock acquisition timeouts and obtain the synchronized keyword lock and other synchronization features are not available
Lock lock = new ReentrantLock(); 
lock.lock(); 
try {    
} finally {    
    lock.unlock(); 
} 
  1. After releasing the lock finally, make sure to get a lock, and ultimately be released

  2. Do not get locked in the process of writing the try block, as if the acquisition occurred at the time of the lock (custom implementations lock) anomaly, an exception is thrown, it would also lead to the release of the lock for no reason

4. Characteristics:

  • Non-blocking attempt to acquire a lock: the current thread to acquire the lock, if this time the lock is not acquired by another thread that successfully acquire and hold the lock;
  • Be interrupted to acquire a lock:
  • Timeout acquire a lock:

5.2 queue synchronizer

AbstractQueuedSynchronizer (AQS) - Synchronizer

The main use synchronizer is inherited, the subclass through inheritance synchronizer and implement its abstract methods to manage the synchronization status, will inevitably have to make changes to the synchronization status in the implementation process of the abstract method, then you need to use synchronizer provided three methods to operate. Synchronizer supports both exclusive access to synchronized state can also support shared access to the synchronized state, so to facilitate the realization of different types of synchronization component (ReentrantLock, ReentrantReadWriteLock, CountDownLatch)

5.3 reentrant lock

Indicates that the lock is capable of supporting a thread locking duplication of resources, in addition, also supports fair and unfair selection when acquiring a lock.

Equitable access lock: that is the longest-waiting thread priority access to the lock.

5.4 Read-Write Lock

5.5 LockSupport Tools

5.6 Condition Interface

Chapter 6 Java concurrent containers and frameworks

Principles and Applications 6.1 ConcurrentHashMap implementation

Thread-safe and efficient HashMap

6.1.1 Why do you want to use?

Use in concurrent programming HashMap may cause an infinite loop , and use thread-safe HashTable inefficient .

  1. Thread-safe HashMap

Multithreading, a put operation will cause an infinite loop, resulting in 100% CPU utilization to solve. Entry list HashMap leads forming an annular data structure.

  1. Inefficient HashTable

Use synchronized to ensure the security thread, but the thread competitive incentives and inefficient;

  1. ConcurrentHashMap the lock segment concurrent access technology can effectively enhance the rate of

First, the data is stored in segments by , and to each piece of data with a lock , a thread holds when one of the end lock access data, other data segments can also be accessible to other threads.

6.1.2 ConcurrentHashMap structure

Segment data structure is ConcurrentHashMap HashEntry and data structures. Segment is a repeatable lock , the lock role; HashEntry for storing key data .

A ConcurrentHashMap contains an array of Segment A Segment contains a HashEntry array, each HashEntry is an element of a linked list structure, each Segment guarding a HashEntry array elements when the elements of the array HashEntry be modified, it must first obtain with his corresponding Segment lock .

FIG class ConcurrentHashMap
FIG configuration ConcurrentHashMap

6.1.3 ConcurrentHashMap initialization

  1. Array initialization segments

  2. Initialization segmentShift and segmentMask

    1. segmentShift up to 16
    2. segmentMask up to 65535
  3. Initialize each segment

6.1.4 Positioning Segment

Because the lock segment Segment ConcurrentHashMap used to protect different segments of the data, and then when the insertion of an Element, must first locate the Segment through a hash algorithm.

Re-hash Objective: to reduce the hash collision, so that the elements can be uniformly distributed in different Segment, thereby improving the efficiency of the access vessel.

Positioning segment

final Segment<K,V> segmentFor(int hash) { return segments[(hash >>> segmentShift) & segmentMask]; }

6.1.5 ConcurrentHashMap operation

  1. get operation

public V get(Object key) { int hash = hash(key.hashCode()); return segmentFor(hash).get(key,hash); }

整个过程不用加锁,除非读到空值才会加锁重读。

原因: get 方法里将要使用到的共享变量都定义为 volatile 类型,能够在线程之间保持可见性,能够被多线程同时读,并且保证不会读到过期的值,但是只能被单线程写(有一种情况可以被多线程写,就是写入的值不依赖于原值),在get 操作里只需要读不需要写共享变量 count 和 value,所以可以不用加锁。

( hash >>> segmentShift ) & segmentMask // 定位 Segment 使用的 hash 函数 int index = hash & (tab.length - 1) // 定位 HashEntry 使用的 hash 函数

  1. put 操作

要写数据,在操作共享变量时,一定要加锁

两个步骤:

  • 判断是否需要对 Segment 里的 HashEntry 数组进行扩容
  • 定位添加元素的位置,然后将其放在 HashEntry 数组里

(1)是否需要扩容

在插入元素前,先判断 Segment 里的 HashEntry 数组是否超过阈值(threshold),若超过,就对 HashEntry 数组扩容。(HashMap 是在插入后对判断是否已经达到容量,再进行扩容)

(2)如何扩容

创建一个容量为原来2倍的数组,然后将原数组里面的元素进行再散列后插入到新的数组里面。ConcurrentHashMap 只对某个 Segment 进行扩容,更高效。

  1. size 操作

先尝试2次通过不锁住 Segment 的方式来统计各个 Segment 大小,如果统计过程中,容器的 count 发生变化,则采用**加锁的方式(把所有的 Segment 的put、get 和clean方法全部锁住)**来统计所有 Segment 的大小。

6.2 ConcurrentLinkedQueue

实现一个线程安全的队列有两种方式:一种是使用阻塞算法,另一种是非阻塞算法。使用阻塞算法的队列可以用一个锁(入队和出队公用一把锁)或两把锁(出入队用不同锁)的方式来实现。非阻塞的算法可以用循环 CAS 的方式来实现。

ConcurrentLinkedQueue 是一个基于链接节点的无界线程安全队列,采用 FIFO 的规则对节点进行排序。

6.2.2 入队列

  1. 入队列的过程

将入队节点添加到队列的尾部。入队过程两件事儿:①定位出尾结点;②使用 CAS 算法将入队节点设置成尾结点的 next 节点,如不成功则重试。

  1. 定位尾结点

tail 节点并不总是尾结点,所以每次入队都需要通过 tail 节点来找到尾结点

  1. 设置入队节点为尾结点
  2. HOPS 的设计意图

控制并减少 tail 节点的更新频率,而不是每次节点入队后都将 tail 节点更新成尾结点,而是大于等于常量 HOPS 时才更新,提高了入队的效率。

6.2.3 出队列

也是通过设置 HOPS 来确定什么时候更新 head 节点,提高出队效率。

6.3 Java 中的阻塞队列

6.3.1 什么是阻塞队列

BlockingQueue:

  1. 阻塞插入: 队列满时,阻塞插入元素的线程,直至不满;
  2. 阻塞移除: 队列空时,阻塞移除元素的线程,直至不空。
方法/处理方式 抛出异常 返回特殊值 一直阻塞 超时退出
插入方法 add ( e ) offer ( e ) put ( e ) offer ( e, time, unit)
移除方法 remove ( ) poll ( ) take ( ) poll ( time, unit)
检查方法 element ( ) peek ( ) 不可用 不可用

6.3.2 Java 里的阻塞队列

JDK 7:

  1. ArrayBlockingQueue

数组实现的有界阻塞队列。默认不保证线程访问的公平性。为了保证公平性,可能降低吞吐量。

  1. LinkedBlockingQueue

链表实现的有界阻塞队列。默认和最大长度为 Integer.MAX_VALUE

  1. PriorityBlockingQueue

支持优先级的无界阻塞队列。

  1. DelayQueue

支持延时获取元素的无界阻塞队列。使用优先队列实现

  1. SynchronousQueue

不存储元素的阻塞队列,每一个 put 操作必须等待一个 take 操作,否则不能继续添加元素。吞吐量高于 LinkedBlockingQueue 和 ArrayBlockingQueue

  1. LinkedTransferQueue

链表结构的无界阻塞队列

  1. LinkedBlockingDeque

链表结构,双向阻塞。可运用在“工作窃取”模式中。

6.3.3 阻塞队列的实现原理

使用通知模式实现。

6.4 Fork/Join 框架

6.4.1 是什么?

是把一个大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。

6.4.2 工作窃取算法

是指从某个线程(这个线程自己的活儿干完了,跑去帮别人干活儿)从其他队列里窃取任务来执行。

使用双端队列,被窃取线程从头部拿任务执行,窃取线程从尾部拿任务执行。

优点:

  • 充分利用线程进行并行运算,减少了线程间的竞争。

缺点:

  • 在某些情况下还是存在竞争,比如队列中只有一个任务时。且该算法会消耗更多的系统资源,比如创建多个线程和多个双端队列。

6.4.3 框架设计

使用两个类完成分割任务,执行任务并合并结果。

①ForkJoinTask:创建ForkJoin 任务

两个子类(用时继承):

  1. RecursiveAction: 用于没有返回结果的任务
  2. RecursiveTask: 用于有返回结果的任务

②ForkJoinPool:ForkJoinTask 需要通过 ForkJoinPool 来执行。

Chapter 7 Java 中的13个原子操作类

7.1 原子更新基本类型类

  • AtomicBoolean
  • AtomicInteger
  • AtomicLong

7.2 原子更新数组

  • AtomicIntegerArray
  • AtomicLongArray
  • AtomicReferenceArray
  • AtomicIntegerArray

7.3 原子更新引用类型

  • AtomicReference
  • AtomicReferenceFiledUpdater
  • AtomicMarkableReference

7.4 原子更新字段类

  • AtomicIntegerFiledUpdater
  • AtomicLongFiledUpdater
  • AtomicStampedReference

Chapter 8 Java 中的并发工具类

CountDownLatch、CyclicBarrier和Semphore 并发流程控制;

Exchanger 线程间交换数据

8.1 等待多线程完成的 CountDownLatch

允许一个线程或多个线程等待其他线程完成操作。

CountDownLatch 的构造函数接收一个 int 类型的参数作为计数器,如果你想等待 N 个点完成,这里就传入 N。调用 countDown 方法,N 就减1。CountDownLatch 的 await 方法会阻塞当前线程,直到 N = 0。N 个点,可以是 N 个线程,也可以是1个线程里面的 N 个执行步骤。用在多线程里面,只需要吧 CountDownLatch 的引用传递到线程里即可。

CountDownLatch 不能重新初始化或修改 CountDownLatch 对象的内部计数器的值。

8.2 同步屏障 CyclicBarrier

一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被拦截的线程才会继续运行(继续执行的线程没有先后顺序,取决于CPU调度)。

8.2.1 简介

默认构造方法是 CyclicBarrier (int parties),其参数表示屏障拦截的线程数量,每个线程调用 await 方法告诉 CyclicBarrier 我已经到达屏障,然后当前线程被阻塞。

还有一个高级构造方法 CyclicBarrier (int parties, Runnable barrierAction),用于在线程到达屏障时,优先执行 barrierAction,方便处理更复杂的业务场景。

8.2.3 CyclicBarrier 与 CountDownLatch 的区别

CountDownLatch 的计数器只能使用一次,但是 CyclicBarrier 的计数器可以使用 reset()方法重置,因此 CyclicBarrier 能处理更为复杂的业务场景。

8.3 控制并发线程数的 Semaphore

用来控制同时访问特定资源的线程数量,通过协调各个线程,以保证合理使用公共资源。

  1. 应用场景

做流量控制,特别是公共资源有限的应用场景,比如数据库连接。

8.4 线程间交换数据的 Exchanger

用于线程间的数据交换。它提供一个同步点,在这个同步点,两个线程可以通过 exchange 方法交换彼此的数据。

可以应用于遗传算法

可以应用于校对工作

Chapter 9 Java中的线程池

好处:

  1. 降低资源消耗。
  2. 提高响应速度。
  3. 提高线程的可管理性。

9.1 线程池的实现原理

处理流程中,先判断核心线程池是否已满,满了再判断工作队列是否已满,满了再判断线程池是否已满,满了再执行拒绝策略。

The main processing flow thread pool

ThreadPoolExecutor 执行 execute()方法:

ThreadPoolExecutor schematic execution

四种情况分析:

  1. 若当前运行的线程少于 coorPoolSize,则创建新线程来执行任务(需要获得全局锁,开销大)
  2. 若运行的线程大于等于 coorPoolSize,则将任务加入 BlockingQueue(大多数时候处于这里)
  3. 若 BlockingQueue 队列已满,则创建新的线程来处理任务(需要获得全局锁,开销大)
  4. 若创建新线程将使当前运行的总线程数超出 maximumPoolSize,那么执行拒绝策略。

工作线程: 线程池创建线程时,会将线程封装成工作线程 Worker,Worker在执行完任务之后还会循环获取工作队列里的任务来执行。

9.2 线程池的使用

9.2.1 线程池创建

通过 ThreadPoolExecutor 创建,很多个参数。

new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, millseconds, runnableTaskQueue, handler)

9.2.2 向线程池提交任务

execute() // 提交不需要返回值的任务,无法判断任务是否被线程池执行成功 submit() // 提交需要返回值的任务

submit(): 线程池会返回一个 future 类型的对象,通过这个对象可以判断任务是否执行成功,且可以通过 future 的 get 方法获取返回值,get 方法会阻塞当前线程直到任务完成,使用 get(long timeout, TimeUnit unit) 会阻塞一段时间后立即返回,这时任务可能还没有执行完成。

9.2.3 关闭线程池

shutdown () or shutdownNow () method.

The principle is to traverse all worker threads in the thread pool, and then one by one call interrupt method to interrupt the thread, so I can not respond to interrupt the task may never stop.

Usually use the shutdown to shut down the thread pool.

If you have questions, please correct me!

Published 16 original articles · won praise 2 · Views 1278

Guess you like

Origin blog.csdn.net/yx185/article/details/103281607