The second concurrent programming of the JUC
Since JDK8 join the class, in order to further optimize read performance, which is characterized by using a read lock, write lock must cooperate [stamp] use
plus interpretation lock
locked write lock
optimistic locking, StampedLock support tryOptimisticRead () method (optimistic reading ), after finished reading need to do a poke check If the check does not indicate a write operation during this period, the data can be used safely, if the check does not pass, you need to re-acquire a read lock to ensure data security.
Semaphores, threads used to restrict access to both the upper limit of shared resources.
- Lock unlock process
Semaphore bit like a parking lot, just like the number of parking permits, obtain permits when the thread is like to get a parking space, and then display the parking lot of free parking spaces minus a
beginning, permits (state) 3, which 5 threads to access resources
hypothesis which thread - 1, thread - 2, thread -4 cas compete successfully, and thread - 0 and thread - 3 competition fails, AQS queue to enter the park blocked
this time thread - 4 release of the permits, state follows
next Thread - 0 of competition success, permits again set to 0, set Thread himself as head node, disconnect your old head node, unpark next --3 node, but the permits are 0, Thread - 3 try not after successfully entering the state park again
用来进行线程同步协作,等待所有线程完成倒计时。
其中构造参数用来初始化等待计数值,await()用来等待计数归零,countDown()用来让计数减一
循环栅栏,用来进行线程协作,等待线程满足某个计数。构造时设置【计数个数】,每个线程执行到某个需要“同步”的时刻调用await()方法进行等待,当等待的线程数满足【计数个数】时,继续执行
线程安全集合类可以分为三大类 :
- 遗留的线程安全集合如 Hashtable、Vector
- 使用Collections装饰的线程安全集合,如 :
- Collections.synchronizedCollection
- Collections.synchronizedList
- Collections.synchronizedMap
- Collections.synchronizedSet
- Collections.synchronizedNavigableMap
- Collections.synchronizedNavigableSet
- Collections.synchronizedMap
- Collections.synchronizedSet
- java.util.concurrent.*
重点介绍java.util.concurrent.*下的线程安全集合类,里面包含三类关键词 :Blocking、CopyOnWrite、Concurrent - Blocking大部分实现基于锁,并提供用来阻塞的方法
- CopyOnWrite 之类容器修改稍相对较重
- Concurrent类型的容器
- 内部很多操作使用cas优化,一般可以提供高吞吐量
- 弱一致性
- 遍历时弱一致性,例如,当利用迭代器遍历时,如果容器发生修改,迭代器任然可以进行进行遍历,这时内存是旧的
- 求大小弱一致性,size操作未必是100%准确
- 读取弱一致性
- 遍历时如果发生了修改,对于非安全容器来讲,使用fail-fast机制也就是让遍历立刻失败,抛出ConcurrentModificationExecption,不再继续遍历
单词计数问题 :
重要属性和内部类
构造器分析
可以看到实现了懒惰初始化,在构造方法中仅仅计算了table的大小,以后在第一次使用时才会真正的创建
get流程分析
put流程
以下数组简称(table),链表简称(bin)
size计算流程
size计算实际发生在put、remove改变集合元素的操作之中
- 没有竞争发生,向baseCount累加计数
- 有竞争发生,新建counterCells,向其中的一个cell累加计数
- counterCells初始有两个cell
- 如果计数竞争比较激烈,会创建新的cell来累加计数
它维护了一个segment数组,每个segment对应一把锁
- 优点 : 如果多个线程访问不同的segment,实际是没有冲突的,这与jdk8中是类似的
- 缺点 :Segments数组默认大小为16,这个容量初始化指定后就不能改变了,并且不是懒得初始化
构造器分析
可以看到ConcurrentHashMap 没有实现懒惰初始化,空间占用不友好
其中 this.segmentShift 和 this.segmentMask的作用是决定将key的hash结果匹配到那个 segment
例如 :根据某一hash值求segment位置,先将高位向低位移动 this.segmentShift位
结果再与this.segmentMask 做位于运算,最终得到1010即下标为10的segment
put流程
segment继承了可重入锁(ReentrantLock),它的put方法为
get流程
get时并未加锁,用了UNSAFE方法保证了可见性,扩容过程中,get先发生就从旧表取内容,get后发生就从新表取内容
size计算流程 - 计算元素个数前,先不加锁计算两次,如果前后两次结果如一样,认为个数正确返回
- 如果不一样,进行重试,重试次数超过3,将所有segment锁住,重新计算个数返回
LinkedBlockingQueue 原理
- 基本的入队出队
初始化链表 last = head = new Node(null);Dummy节点用来占位,item为null
当一个节点入队 last = last.next = node;
再来一个节点入队 last = last.next = node
出队
h = head
first = h.next
h.next = h
head = first
E x = first.item;
first.item = null;
return x; - 加锁分析
高明之处在于用了两把锁和dummy节点
- 用一把锁,同一时刻,最多只允许有一个线程(生产者或消费者,二选一)执行
- 用两把锁,同一时刻,可以允许两个线程同时(一个生产者与一个消费者)执行
- 消费者与消费者线程任然串行
- 生产者与生产者线程任然串行
线程安全分析
- 当节点总数大于2时(包括dummy节点),putLock保证的是last节点的线程安全,takeLock保证的是head节点的线程安全。两把锁保证了入队和出队没有竞争
- 当节点总数等于2时(即一个dummy节点,一个正常节点)这时候,仍然是两把锁锁两个对象,不会竞争
- 当节点总数等于1时(就一个dummy节点)这时take线程会被notEmpty条件阻塞,有竞争,会阻塞
put操作
take操作
主要列举 LinkedBlockingQueue与ArrayBlockingQueue的性能比较
- Linked支持有界,Array强制有界
- Linked实现是链表,Array实现是数组
- Linked lazy, and Node Array need to initialize the array in advance
- Linked into each team will generate a new Node, and Array of Node is to create good advance
- Linked two locks, Array a lock
ConcurrentLinkedQueue design and LinkedBlockingQueue very much like, but also
- Two [lock], the same time, allows two threads (a producer and a consumer) execution
- Introducing dummy node lock to make two [future] is locked different objects, to avoid competition
- Only this cas [lock] used to achieve
in fact, still very wide range of applications ConcurrentLinkedQueue
example, before talking about the structure of Tomcat Connector, Acceptor as producers deliver event information to consumers when Poller, it uses ConcurrentLinkedQueue to the SocketChannel Poller use
CopyOnWriteArraySet vest its
underlying implementation uses a copy writing idea underlying array CRUD operations will copy, change operation performed on the new array, then it does not affect the reading of other concurrent threads, separate read and write.
Here's the source code version Java11, used in Java1.8 is reentrant lock and not synchronized, other read operations are not locked, for example:
for reading and writing [less] application scenarios
get weakly consistent
iterator weak consistency
Do not feel bad weak consistency
- MVCC database is weak consistency of performance
- High concurrency and consistency is contradictory, need to weigh