本文纲要
HashMap线程不安全演示Hashtable线程安全与原理ConcurrentHashMap基本使用ConcurrentHashMap 1.7原理(分段锁)ConcurrentHashMap 1.8原理(CAS+synchronized)CountDownLatch使用场景与原理Semaphore使用场景与原理
HashMap线程不安全演示
1 ) 项目结构
text
src/
└── com/
└── wb/
├── mymap/
│ ├── MyHashMapDemo.java
│ ├── MyHashtableDemo.java
│ └── MyConcurrentHashMapDemo.java
├── mycountdownlatch/
│ ├── MotherThread.java
│ ├── ChileThread1.java
│ ├── ChileThread2.java
│ ├── ChileThread3.java
│ └── MyCountDownLatchDemo.java
└── mysemaphore/
├── MyRunnable.java
└── MySemaphoreDemo.java
在多线程环境下,HashMap 因为线程抢夺CPU的随机性,会出现数据安全问题。
例如两个线程同时 put 数据,可能导致某个键的值为 null。
java
package com.wb.mymap;
import java.util.HashMap;
public class MyHashMapDemo {
public static void main(String[] args) throws InterruptedException {
HashMap<String, String> hm = new HashMap<>();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 25; i++) {
hm.put(i + "", i + "");
}
});
Thread t2 = new Thread(() -> {
for (int i = 25; i < 51; i++) {
hm.put(i + "", i + "");
}
});
t1.start();
t2.start();
System.out.println("----------------------------");
// 为了t1和t2能把数据全部添加完毕
Thread.sleep(1000);
// 预期输出: 0 1 2 3 ... 50
for (int i = 0; i < 51; i++) {
System.out.println(hm.get(i + ""));
}
}
}
运行结果可能在某处打印 null,说明 HashMap 在多线程下不安全。
Hashtable线程安全与原理
Hashtable 可以保证线程安全,但效率极低,因为它在几乎所有方法上都加了 synchronized 关键字,采用悲观锁,操作时会将整张表锁住。
java
package com.wb.mymap;
import java.util.Hashtable;
public class MyHashtableDemo {
public static void main(String[] args) throws InterruptedException {
Hashtable<String, String> hm = new Hashtable<>();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 25; i++) {
hm.put(i + "", i + "");
}
});
Thread t2 = new Thread(() -> {
for (int i = 25; i < 51; i++) {
hm.put(i + "", i + "");
}
});
t1.start();
t2.start();
System.out.println("----------------------------");
Thread.sleep(1000);
for (int i = 0; i < 51; i++) {
System.out.println(hm.get(i + ""));
}
}
}
多次运行结果均完整,不会出现 null。
Hashtable锁机制示意
#mermaid-svg-DIVo9Xn3GKg7uA60{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DIVo9Xn3GKg7uA60 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DIVo9Xn3GKg7uA60 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DIVo9Xn3GKg7uA60 .error-icon{fill:#552222;}#mermaid-svg-DIVo9Xn3GKg7uA60 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DIVo9Xn3GKg7uA60 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DIVo9Xn3GKg7uA60 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DIVo9Xn3GKg7uA60 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DIVo9Xn3GKg7uA60 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DIVo9Xn3GKg7uA60 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DIVo9Xn3GKg7uA60 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DIVo9Xn3GKg7uA60 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DIVo9Xn3GKg7uA60 .marker.cross{stroke:#333333;}#mermaid-svg-DIVo9Xn3GKg7uA60 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DIVo9Xn3GKg7uA60 p{margin:0;}#mermaid-svg-DIVo9Xn3GKg7uA60 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-DIVo9Xn3GKg7uA60 .cluster-label text{fill:#333;}#mermaid-svg-DIVo9Xn3GKg7uA60 .cluster-label span{color:#333;}#mermaid-svg-DIVo9Xn3GKg7uA60 .cluster-label span p{background-color:transparent;}#mermaid-svg-DIVo9Xn3GKg7uA60 .label text,#mermaid-svg-DIVo9Xn3GKg7uA60 span{fill:#333;color:#333;}#mermaid-svg-DIVo9Xn3GKg7uA60 .node rect,#mermaid-svg-DIVo9Xn3GKg7uA60 .node circle,#mermaid-svg-DIVo9Xn3GKg7uA60 .node ellipse,#mermaid-svg-DIVo9Xn3GKg7uA60 .node polygon,#mermaid-svg-DIVo9Xn3GKg7uA60 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DIVo9Xn3GKg7uA60 .rough-node .label text,#mermaid-svg-DIVo9Xn3GKg7uA60 .node .label text,#mermaid-svg-DIVo9Xn3GKg7uA60 .image-shape .label,#mermaid-svg-DIVo9Xn3GKg7uA60 .icon-shape .label{text-anchor:middle;}#mermaid-svg-DIVo9Xn3GKg7uA60 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-DIVo9Xn3GKg7uA60 .rough-node .label,#mermaid-svg-DIVo9Xn3GKg7uA60 .node .label,#mermaid-svg-DIVo9Xn3GKg7uA60 .image-shape .label,#mermaid-svg-DIVo9Xn3GKg7uA60 .icon-shape .label{text-align:center;}#mermaid-svg-DIVo9Xn3GKg7uA60 .node.clickable{cursor:pointer;}#mermaid-svg-DIVo9Xn3GKg7uA60 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-DIVo9Xn3GKg7uA60 .arrowheadPath{fill:#333333;}#mermaid-svg-DIVo9Xn3GKg7uA60 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DIVo9Xn3GKg7uA60 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DIVo9Xn3GKg7uA60 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DIVo9Xn3GKg7uA60 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-DIVo9Xn3GKg7uA60 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DIVo9Xn3GKg7uA60 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-DIVo9Xn3GKg7uA60 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DIVo9Xn3GKg7uA60 .cluster text{fill:#333;}#mermaid-svg-DIVo9Xn3GKg7uA60 .cluster span{color:#333;}#mermaid-svg-DIVo9Xn3GKg7uA60 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-DIVo9Xn3GKg7uA60 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-DIVo9Xn3GKg7uA60 rect.text{fill:none;stroke-width:0;}#mermaid-svg-DIVo9Xn3GKg7uA60 .icon-shape,#mermaid-svg-DIVo9Xn3GKg7uA60 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DIVo9Xn3GKg7uA60 .icon-shape p,#mermaid-svg-DIVo9Xn3GKg7uA60 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-DIVo9Xn3GKg7uA60 .icon-shape .label rect,#mermaid-svg-DIVo9Xn3GKg7uA60 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DIVo9Xn3GKg7uA60 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-DIVo9Xn3GKg7uA60 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-DIVo9Xn3GKg7uA60 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Hashtable
锁住
等待
释放锁
线程1
整张表
线程2
线程2操作
小结: Hashtable 底层是哈希表结构(数组+链表),默认长度16,加载因子0.75。因为它对所有访问方法使用 synchronized,一旦有线程访问,整张表被锁,其他线程只能等待,效率低下。
ConcurrentHashMap基本使用
JDK1.5 提供了 ConcurrentHashMap,它既保证线程安全,又兼顾效率。它实现了 Map 接口,可以使用 put、keySet、entrySet 等方法。
java
package com.wb.mymap;
import java.util.concurrent.ConcurrentHashMap;
public class MyConcurrentHashMapDemo {
public static void main(String[] args) throws InterruptedException {
ConcurrentHashMap<String, String> hm = new ConcurrentHashMap<>();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 25; i++) {
hm.put(i + "", i + "");
}
});
Thread t2 = new Thread(() -> {
for (int i = 25; i < 51; i++) {
hm.put(i + "", i + "");
}
});
t1.start();
t2.start();
System.out.println("----------------------------");
Thread.sleep(1000);
for (int i = 0; i < 51; i++) {
System.out.println(hm.get(i + ""));
}
}
}
运行结果数据完整,无 null,同时效率高于 Hashtable。
ConcurrentHashMap 1.7原理
在JDK1.7中,ConcurrentHashMap 采用分段锁机制,内部由 Segment 数组和 HashEntry 数组组成。
1 ) 创建对象
- 创建一个长度为16的
Segment数组,加载因子0.75。 - 同时创建一个长度为2的小数组(
HashEntry),并将其地址赋给Segment[0],其他索引为null。该小数组作为模板。
2 ) 添加元素流程
- 根据键的哈希值计算在
Segment数组中的索引(第一次哈希)。 - 若该索引为
null,则按照模板创建新的HashEntry数组(默认长度2),并赋值给该索引。 - 进行二次哈希,计算元素在
HashEntry数组中的位置。 - 若该位置为
null,直接存入;若不为null,则调用equals比较,相同则覆盖,不同则形成链表(哈希桶)。 - 当
HashEntry数组元素达到阈值(长度×0.75)时,会扩容为原来的2倍(Segment数组本身不扩容)。
3 ) 线程安全机制
- 每个
Segment对象都有独立的锁,ConcurrentHashMap通过对Segment加锁实现并发控制。 - 默认16个
Segment,最多允许16个线程同时操作不同的Segment。
#mermaid-svg-4kdLvloTndyE1t6Y{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4kdLvloTndyE1t6Y .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4kdLvloTndyE1t6Y .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4kdLvloTndyE1t6Y .error-icon{fill:#552222;}#mermaid-svg-4kdLvloTndyE1t6Y .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4kdLvloTndyE1t6Y .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4kdLvloTndyE1t6Y .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4kdLvloTndyE1t6Y .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4kdLvloTndyE1t6Y .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4kdLvloTndyE1t6Y .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4kdLvloTndyE1t6Y .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4kdLvloTndyE1t6Y .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4kdLvloTndyE1t6Y .marker.cross{stroke:#333333;}#mermaid-svg-4kdLvloTndyE1t6Y svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4kdLvloTndyE1t6Y p{margin:0;}#mermaid-svg-4kdLvloTndyE1t6Y .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4kdLvloTndyE1t6Y .cluster-label text{fill:#333;}#mermaid-svg-4kdLvloTndyE1t6Y .cluster-label span{color:#333;}#mermaid-svg-4kdLvloTndyE1t6Y .cluster-label span p{background-color:transparent;}#mermaid-svg-4kdLvloTndyE1t6Y .label text,#mermaid-svg-4kdLvloTndyE1t6Y span{fill:#333;color:#333;}#mermaid-svg-4kdLvloTndyE1t6Y .node rect,#mermaid-svg-4kdLvloTndyE1t6Y .node circle,#mermaid-svg-4kdLvloTndyE1t6Y .node ellipse,#mermaid-svg-4kdLvloTndyE1t6Y .node polygon,#mermaid-svg-4kdLvloTndyE1t6Y .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4kdLvloTndyE1t6Y .rough-node .label text,#mermaid-svg-4kdLvloTndyE1t6Y .node .label text,#mermaid-svg-4kdLvloTndyE1t6Y .image-shape .label,#mermaid-svg-4kdLvloTndyE1t6Y .icon-shape .label{text-anchor:middle;}#mermaid-svg-4kdLvloTndyE1t6Y .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4kdLvloTndyE1t6Y .rough-node .label,#mermaid-svg-4kdLvloTndyE1t6Y .node .label,#mermaid-svg-4kdLvloTndyE1t6Y .image-shape .label,#mermaid-svg-4kdLvloTndyE1t6Y .icon-shape .label{text-align:center;}#mermaid-svg-4kdLvloTndyE1t6Y .node.clickable{cursor:pointer;}#mermaid-svg-4kdLvloTndyE1t6Y .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4kdLvloTndyE1t6Y .arrowheadPath{fill:#333333;}#mermaid-svg-4kdLvloTndyE1t6Y .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4kdLvloTndyE1t6Y .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4kdLvloTndyE1t6Y .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4kdLvloTndyE1t6Y .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4kdLvloTndyE1t6Y .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4kdLvloTndyE1t6Y .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4kdLvloTndyE1t6Y .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4kdLvloTndyE1t6Y .cluster text{fill:#333;}#mermaid-svg-4kdLvloTndyE1t6Y .cluster span{color:#333;}#mermaid-svg-4kdLvloTndyE1t6Y div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-4kdLvloTndyE1t6Y .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4kdLvloTndyE1t6Y rect.text{fill:none;stroke-width:0;}#mermaid-svg-4kdLvloTndyE1t6Y .icon-shape,#mermaid-svg-4kdLvloTndyE1t6Y .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4kdLvloTndyE1t6Y .icon-shape p,#mermaid-svg-4kdLvloTndyE1t6Y .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4kdLvloTndyE1t6Y .icon-shape .label rect,#mermaid-svg-4kdLvloTndyE1t6Y .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4kdLvloTndyE1t6Y .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4kdLvloTndyE1t6Y .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4kdLvloTndyE1t6Y :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 锁机制
ConcurrentHashMap1.7
锁住
锁住
等待S1
Segment数组
Segment0: HashEntry数组
Segment1: null
...
HashEntry
链表节点
链表节点
线程1
线程2
线程3
小结:
- 大数组(
Segment)创建后不可扩容,小数组(HashEntry)可扩容2倍。 - 通过分段锁(
ReentrantLock)保证线程安全,不同Segment可并发操作。
ConcurrentHashMap 1.8原理
JDK1.8改进了 ConcurrentHashMap,底层采用数组+链表+红黑树结构,与 HashMap 1.8类似,但使用 CAS + synchronized 保证线程安全。
1 ) 初始化
- 空参构造什么也不做,延迟初始化,在第一次
put时创建数组(默认容量16,加载因子0.75)。
2) 添加元素流程
- 根据键的哈希值计算数组索引。
- 若该索引为
null,使用CAS算法直接将节点放入数组。 - 若不为
null,则使用volatile关键字获取该位置的最新节点,并采用synchronized锁住该节点(链表头节点或红黑树根节点)。 - 若是链表,遍历并比较
equals,相同则替换,不同则插入链表尾部;若链表长度 ≥ 8,转换为红黑树。 - 若是红黑树,按树的方式插入。
3 )线程安全机制
- 不锁整张表,只锁当前操作的链表或红黑树头节点,其他线程可以同时操作其他桶。
- 使用
synchronized代码块锁住头节点对象。
#mermaid-svg-MCYO4RKOhaeZZ8gz{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MCYO4RKOhaeZZ8gz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MCYO4RKOhaeZZ8gz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MCYO4RKOhaeZZ8gz .error-icon{fill:#552222;}#mermaid-svg-MCYO4RKOhaeZZ8gz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MCYO4RKOhaeZZ8gz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MCYO4RKOhaeZZ8gz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MCYO4RKOhaeZZ8gz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MCYO4RKOhaeZZ8gz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MCYO4RKOhaeZZ8gz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MCYO4RKOhaeZZ8gz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MCYO4RKOhaeZZ8gz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MCYO4RKOhaeZZ8gz .marker.cross{stroke:#333333;}#mermaid-svg-MCYO4RKOhaeZZ8gz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MCYO4RKOhaeZZ8gz p{margin:0;}#mermaid-svg-MCYO4RKOhaeZZ8gz .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-MCYO4RKOhaeZZ8gz .cluster-label text{fill:#333;}#mermaid-svg-MCYO4RKOhaeZZ8gz .cluster-label span{color:#333;}#mermaid-svg-MCYO4RKOhaeZZ8gz .cluster-label span p{background-color:transparent;}#mermaid-svg-MCYO4RKOhaeZZ8gz .label text,#mermaid-svg-MCYO4RKOhaeZZ8gz span{fill:#333;color:#333;}#mermaid-svg-MCYO4RKOhaeZZ8gz .node rect,#mermaid-svg-MCYO4RKOhaeZZ8gz .node circle,#mermaid-svg-MCYO4RKOhaeZZ8gz .node ellipse,#mermaid-svg-MCYO4RKOhaeZZ8gz .node polygon,#mermaid-svg-MCYO4RKOhaeZZ8gz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MCYO4RKOhaeZZ8gz .rough-node .label text,#mermaid-svg-MCYO4RKOhaeZZ8gz .node .label text,#mermaid-svg-MCYO4RKOhaeZZ8gz .image-shape .label,#mermaid-svg-MCYO4RKOhaeZZ8gz .icon-shape .label{text-anchor:middle;}#mermaid-svg-MCYO4RKOhaeZZ8gz .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MCYO4RKOhaeZZ8gz .rough-node .label,#mermaid-svg-MCYO4RKOhaeZZ8gz .node .label,#mermaid-svg-MCYO4RKOhaeZZ8gz .image-shape .label,#mermaid-svg-MCYO4RKOhaeZZ8gz .icon-shape .label{text-align:center;}#mermaid-svg-MCYO4RKOhaeZZ8gz .node.clickable{cursor:pointer;}#mermaid-svg-MCYO4RKOhaeZZ8gz .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-MCYO4RKOhaeZZ8gz .arrowheadPath{fill:#333333;}#mermaid-svg-MCYO4RKOhaeZZ8gz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MCYO4RKOhaeZZ8gz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MCYO4RKOhaeZZ8gz .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MCYO4RKOhaeZZ8gz .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-MCYO4RKOhaeZZ8gz .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MCYO4RKOhaeZZ8gz .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-MCYO4RKOhaeZZ8gz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MCYO4RKOhaeZZ8gz .cluster text{fill:#333;}#mermaid-svg-MCYO4RKOhaeZZ8gz .cluster span{color:#333;}#mermaid-svg-MCYO4RKOhaeZZ8gz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MCYO4RKOhaeZZ8gz .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-MCYO4RKOhaeZZ8gz rect.text{fill:none;stroke-width:0;}#mermaid-svg-MCYO4RKOhaeZZ8gz .icon-shape,#mermaid-svg-MCYO4RKOhaeZZ8gz .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MCYO4RKOhaeZZ8gz .icon-shape p,#mermaid-svg-MCYO4RKOhaeZZ8gz .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-MCYO4RKOhaeZZ8gz .icon-shape .label rect,#mermaid-svg-MCYO4RKOhaeZZ8gz .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MCYO4RKOhaeZZ8gz .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MCYO4RKOhaeZZ8gz .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MCYO4RKOhaeZZ8gz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 锁机制
ConcurrentHashMap1.8
synchronized
synchronized
等待D
数组
桶0: 链表
桶1: 红黑树
节点1
节点2
根节点
子节点
线程1
线程2
线程3
小结:
- 空参构造延迟初始化,首次
put创建数组。 - 通过
CAS操作向空桶添加节点。 - 通过
synchronized锁住桶头节点,实现细粒度并发控制。 - 链表长度 ≥ 8 时转为红黑树,提升查询效率。
CountDownLatch使用场景与原理
CountDownLatch 允许一条线程等待其他多条线程执行完毕后再执行。
经典场景:妈妈等待三个孩子吃完饺子后收拾碗筷。
1 )核心方法
| 方法 | 说明 |
|---|---|
CountDownLatch(int count) |
构造方法,count 为等待线程数量(底层定义计数器) |
await() |
让当前线程等待,直到计数器变为0 |
countDown() |
每调用一次,计数器减1;当计数器为0时,唤醒等待线程 |
2 ) 代码实现
java
package com.wb.mycountdownlatch;
public class MotherThread extends Thread {
private CountDownLatch countDownLatch;
public MotherThread(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
// 1.等待
try {
// 当计数器变成0时,会自动唤醒这里等待的线程
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 2.收拾碗筷
System.out.println("妈妈在收拾碗筷");
}
}
```java
package com.wb.mycountdownlatch;
public class ChileThread1 extends Thread {
private CountDownLatch countDownLatch;
public ChileThread1(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
// 1.吃饺子
for (int i = 1; i <= 10; i++) {
System.out.println(getName() + "在吃第" + i + "个饺子");
}
// 2.吃完说一声
// 每一次countDown方法的时候,就让计数器-1
countDownLatch.countDown();
}
}
其余孩子线程(ChileThread2、ChileThread3)类似,分别吃15个和20个饺子。
java
package com.wb.mycountdownlatch;
import java.util.concurrent.CountDownLatch;
public class MyCountDownLatchDemo {
public static void main(String[] args) {
// 1.创建CountDownLatch对象,需要传递给四个线程。
// 在底层定义了一个计数器,此时计数器的值为3
CountDownLatch countDownLatch = new CountDownLatch(3);
// 2.创建四个线程对象并开启
MotherThread motherThread = new MotherThread(countDownLatch);
motherThread.start();
ChileThread1 t1 = new ChileThread1(countDownLatch);
t1.setName("小明");
ChileThread2 t2 = new ChileThread2(countDownLatch);
t2.setName("小红");
ChileThread3 t3 = new ChileThread3(countDownLatch);
t3.setName("小刚");
t1.start();
t2.start();
t3.start();
}
}
3 ) 执行流程
小刚 小红 小明 妈妈线程 小刚 小红 小明 妈妈线程 #mermaid-svg-RtjWSeGntM2bPK0x{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RtjWSeGntM2bPK0x .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RtjWSeGntM2bPK0x .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RtjWSeGntM2bPK0x .error-icon{fill:#552222;}#mermaid-svg-RtjWSeGntM2bPK0x .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RtjWSeGntM2bPK0x .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RtjWSeGntM2bPK0x .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RtjWSeGntM2bPK0x .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RtjWSeGntM2bPK0x .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RtjWSeGntM2bPK0x .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RtjWSeGntM2bPK0x .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RtjWSeGntM2bPK0x .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RtjWSeGntM2bPK0x .marker.cross{stroke:#333333;}#mermaid-svg-RtjWSeGntM2bPK0x svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RtjWSeGntM2bPK0x p{margin:0;}#mermaid-svg-RtjWSeGntM2bPK0x .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-RtjWSeGntM2bPK0x text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-RtjWSeGntM2bPK0x .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-RtjWSeGntM2bPK0x .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-RtjWSeGntM2bPK0x .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-RtjWSeGntM2bPK0x .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-RtjWSeGntM2bPK0x #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-RtjWSeGntM2bPK0x .sequenceNumber{fill:white;}#mermaid-svg-RtjWSeGntM2bPK0x #sequencenumber{fill:#333;}#mermaid-svg-RtjWSeGntM2bPK0x #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-RtjWSeGntM2bPK0x .messageText{fill:#333;stroke:none;}#mermaid-svg-RtjWSeGntM2bPK0x .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-RtjWSeGntM2bPK0x .labelText,#mermaid-svg-RtjWSeGntM2bPK0x .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-RtjWSeGntM2bPK0x .loopText,#mermaid-svg-RtjWSeGntM2bPK0x .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-RtjWSeGntM2bPK0x .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-RtjWSeGntM2bPK0x .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-RtjWSeGntM2bPK0x .noteText,#mermaid-svg-RtjWSeGntM2bPK0x .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-RtjWSeGntM2bPK0x .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-RtjWSeGntM2bPK0x .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-RtjWSeGntM2bPK0x .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-RtjWSeGntM2bPK0x .actorPopupMenu{position:absolute;}#mermaid-svg-RtjWSeGntM2bPK0x .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-RtjWSeGntM2bPK0x .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-RtjWSeGntM2bPK0x .actor-man circle,#mermaid-svg-RtjWSeGntM2bPK0x line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-RtjWSeGntM2bPK0x :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} await() 等待吃饺子(10个)countDown() 计数器2吃饺子(15个)countDown() 计数器1吃饺子(20个)countDown() 计数器0被唤醒,收拾碗筷
小结: CountDownLatch 通过内部计数器实现线程等待,一次递减,计数器为0时唤醒等待线程,常用于主线程等待子线程完成的场景。
Semaphore使用场景与原理
Semaphore 用于控制同时访问特定资源的线程数量,如同单行道最多允许2辆车同时通行,发通行证的管理员。
1 ) 核心方法
| 方法 | 说明 |
|---|---|
Semaphore(int permits) |
构造方法,permits 为最大通行证数量(允许同时执行的线程数) |
acquire() |
获得通行证,若无则线程等待 |
release() |
归还通行证,唤醒等待线程 |
2 ) 代码实现
java
package com.wb.mysemaphore;
import java.util.concurrent.Semaphore;
public class MyRunnable implements Runnable {
// 1.获得管理员对象,最多允许2个线程同时执行
private Semaphore semaphore = new Semaphore(2);
@Override
public void run() {
// 2.获得通行证
try {
semaphore.acquire();
// 3.开始行驶
System.out.println("获得了通行证开始行驶");
Thread.sleep(2000);
System.out.println("归还通行证");
// 4.归还通行证
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
java
package com.wb.mysemaphore;
public class MySemaphoreDemo {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
for (int i = 0; i < 100; i++) {
new Thread(mr).start();
}
}
}
3 ) 执行效果
#mermaid-svg-aqaghoNPHWIPFNB8{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-aqaghoNPHWIPFNB8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-aqaghoNPHWIPFNB8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-aqaghoNPHWIPFNB8 .error-icon{fill:#552222;}#mermaid-svg-aqaghoNPHWIPFNB8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-aqaghoNPHWIPFNB8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-aqaghoNPHWIPFNB8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-aqaghoNPHWIPFNB8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-aqaghoNPHWIPFNB8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-aqaghoNPHWIPFNB8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-aqaghoNPHWIPFNB8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-aqaghoNPHWIPFNB8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-aqaghoNPHWIPFNB8 .marker.cross{stroke:#333333;}#mermaid-svg-aqaghoNPHWIPFNB8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-aqaghoNPHWIPFNB8 p{margin:0;}#mermaid-svg-aqaghoNPHWIPFNB8 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-aqaghoNPHWIPFNB8 .cluster-label text{fill:#333;}#mermaid-svg-aqaghoNPHWIPFNB8 .cluster-label span{color:#333;}#mermaid-svg-aqaghoNPHWIPFNB8 .cluster-label span p{background-color:transparent;}#mermaid-svg-aqaghoNPHWIPFNB8 .label text,#mermaid-svg-aqaghoNPHWIPFNB8 span{fill:#333;color:#333;}#mermaid-svg-aqaghoNPHWIPFNB8 .node rect,#mermaid-svg-aqaghoNPHWIPFNB8 .node circle,#mermaid-svg-aqaghoNPHWIPFNB8 .node ellipse,#mermaid-svg-aqaghoNPHWIPFNB8 .node polygon,#mermaid-svg-aqaghoNPHWIPFNB8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-aqaghoNPHWIPFNB8 .rough-node .label text,#mermaid-svg-aqaghoNPHWIPFNB8 .node .label text,#mermaid-svg-aqaghoNPHWIPFNB8 .image-shape .label,#mermaid-svg-aqaghoNPHWIPFNB8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-aqaghoNPHWIPFNB8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-aqaghoNPHWIPFNB8 .rough-node .label,#mermaid-svg-aqaghoNPHWIPFNB8 .node .label,#mermaid-svg-aqaghoNPHWIPFNB8 .image-shape .label,#mermaid-svg-aqaghoNPHWIPFNB8 .icon-shape .label{text-align:center;}#mermaid-svg-aqaghoNPHWIPFNB8 .node.clickable{cursor:pointer;}#mermaid-svg-aqaghoNPHWIPFNB8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-aqaghoNPHWIPFNB8 .arrowheadPath{fill:#333333;}#mermaid-svg-aqaghoNPHWIPFNB8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-aqaghoNPHWIPFNB8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-aqaghoNPHWIPFNB8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-aqaghoNPHWIPFNB8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-aqaghoNPHWIPFNB8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-aqaghoNPHWIPFNB8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-aqaghoNPHWIPFNB8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-aqaghoNPHWIPFNB8 .cluster text{fill:#333;}#mermaid-svg-aqaghoNPHWIPFNB8 .cluster span{color:#333;}#mermaid-svg-aqaghoNPHWIPFNB8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-aqaghoNPHWIPFNB8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-aqaghoNPHWIPFNB8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-aqaghoNPHWIPFNB8 .icon-shape,#mermaid-svg-aqaghoNPHWIPFNB8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-aqaghoNPHWIPFNB8 .icon-shape p,#mermaid-svg-aqaghoNPHWIPFNB8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-aqaghoNPHWIPFNB8 .icon-shape .label rect,#mermaid-svg-aqaghoNPHWIPFNB8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-aqaghoNPHWIPFNB8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-aqaghoNPHWIPFNB8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-aqaghoNPHWIPFNB8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 100个线程
Semaphore允许2个通过
线程1执行
线程2执行
release
release
唤醒等待线程
新线程进入
运行结果:最多同时有2个线程打印"获得了通行证开始行驶",执行完归还后,下一个线程才能进入。
总结
Semaphore 通过许可证数量控制并发数,使用时先 acquire() 获取许可,执行完毕 release() 归还,保证同时访问资源的线程数不超过指定值。
以上是 Java 并发工具类中 Hashtable、ConcurrentHashMap、CountDownLatch 和 Semaphore 的核心知识点,通过原理图解和代码示例帮助快速入门。