[TOC]
# 1. 线程安全的集合/映射/队列
下面列出的集合/映射/队列都是线程安全的。
* java.util.concurrent.ConcurrentLinkedQueue< E> 5.0
* ConcurrentLinkedQueue< E>()
构造一个可以被多线程安全访问的无边界非阻塞的队列。
* java.utilc.concurrent.ConcurrentSkipListSet< E> 6
* ConcurrentSkipListSet< E>()
* ConcurrentSkipListSet< E>(Comparator<? super E> comp
构造一个可以被多线程安全访问的有序集。第一个构造器要求元素实现Comparable接口。
* java.util.concurrent.ConcurrentHashMap< K, V> 5.0
* ConcurrentHashMap< K, V>()
* ConcurrentHashMap< K, V>(int initialCapacity)
* ConcurrentHashMap< K, V>(int initialCapacity, float loadFactor, int concurrecyLevel)
构造一个可以被多线程安全访问的散列映射表,不允许存储null。
`initialCapacity`:集合的初始容量。默认为16。
`loadFactor`:控制调整,如果每一个桶的平均负载超过这个因子,表的大小将被重新调整。默认值0.75。
`concurrencyLevel`:并发写者程序的数目。
* java.util.concurrent.ConcurrentSkipListMap< K, V> 6
* ConcurrrentSkipListMap< K, V>()
* ConcurrentSkipListMap< K, V>(Comparator< ? super K> comp)
构造一个可以被多线程安全访问的有序的映射表。第一个构造器要求键实现Comparable接口。
<br/>
# 2. 映射表的更新
更新:将一个映射表的键对应的旧值,更新为新值。
<br/>
**1. ConcurrentHashMap的更新**
```java
public class ConcurrentHashMapTest {
/**
* 调用replace方法,与do/while循环组合,它会以原子方式用一个新值替换原值,
* 如果替换成功,返回true。前提是之前没有其它线程把原值替换为其它值。
*/
private void replaceTest() {
ConcurrentHashMap<String, Long> map = new ConcurrentHashMap<>(1);
map.put("one", 100L);
//100
System.out.println(map.get("one"));
Long oldValue = new Long(0L);
Long newValue = new Long(0L);
do {
oldValue = map.get("one");
newValue = oldValue == null ? 1L : oldValue + 100L;
} while (!map.replace("one", oldValue, newValue));
//直到更新成功,才退出while循环
//100
System.out.println(map.get("one"));
}
/**
* 调用putIfAbsent方法更新。没有原值才更新,否则不更新。
*/
private void putIfAbsentTest() {
ConcurrentHashMap<String, Long> map = new ConcurrentHashMap<>(1);
map.put("one", 1L);
//1
System.out.println(map.get("one"));
//null
System.out.println(map.get("two"));
map.putIfAbsent("one", 2L);
map.putIfAbsent("two", 2L);
//1
System.out.println(map.get("one"));
//2
System.out.println(map.get("two"));
}
/**
* 调用compute方法更新
*/
private void computeTest() {
ConcurrentHashMap<String, Long> map = new ConcurrentHashMap<>(1);
map.put("one", 1L);
//1
System.out.println(map.get("one"));
map.compute("one", (k, v) -> v == null ? 0L : v + 1L);
//2
System.out.println(map.get("one"));
}
/**
* 调用merge方法更新。
*/
private void mergeTest() {
ConcurrentHashMap<String, Long> map = new ConcurrentHashMap<>(1);
map.put("one", 1L);
//1
System.out.println(map.get("one"));
//null
System.out.println(map.get("two"));
//如果键不存在,则用1L为初始值
//如果键存在,则将使用提供的方法来结合原值和初始值
//sum方法,它会将初始值与原值进行和操作
map.merge("one", 1L, Long::sum);
map.merge("two", 1L, Long::sum);
//2
System.out.println(map.get("one"));
//1
System.out.println(map.get("two"));
}
/**
* 调用computeIfPresent方法更新,有原值才更新,否则不更新。
*/
private void computeIfPresentTest() {
ConcurrentHashMap<String, Long> map = new ConcurrentHashMap<>(1);
map.put("one", 1L);
//1
System.out.println(map.get("one"));
//null
System.out.println(map.get("two"));
map.computeIfPresent("one", (k, v) -> v + 1);
map.computeIfPresent("two", (k, v) -> v + 1);
//2
System.out.println(map.get("one"));
//null
System.out.println(map.get("two"));
}
/**
* 调用computeIfAbsent方法更新,没有原值才更新,否则不更新。
*/
private void computeIfAbsentTest() {
ConcurrentHashMap<String, Long> map = new ConcurrentHashMap<>(1);
map.put("one", 1L);
//1
System.out.println(map.get("one"));
//null
System.out.println(map.get("two"));
map.computeIfAbsent("one", k -> 999L);
map.computeIfAbsent("two", k -> 1L);
//1
System.out.println(map.get("one"));
//1
System.out.println(map.get("two"));
}
public static void main(String[] args) {
ConcurrentHashMapTest test = new ConcurrentHashMapTest();
test.replaceTest();
//test.putIfAbsentTest();
//test.computeTest();
//test.mergeTest();
//test.computeIfPresentTest();
//test.computeIfAbsentTest();
}
}
```
<br/>
# 3. 映射表的批操作
总共有三类不同的批操作,每类批操作有4种不同的批处理方式。
![](https://img.kancloud.cn/a6/8a/a68a3dc6f6522dde35cc4e6ef26b7560_935x334.png)
* 搜索:为每个键,或值提供一个函数。
* 归约:组合所有的键,或值。
* forEach:为所有的键、或值提供一个函数。
映射表的批操作方法都有一个 threshold 阈值,如果映射表的元素多于这个阈值,则并行完成批操作;如果阈值为`Long.MAX_VALUE`,则只允许一个线程操作;如果阈值为`1`,则允许许多线程操作。
<br/>
所有的批操作方法都需要自定义一个函数,然后批操作方法就会自动调用你定义的方法去处理映射表。
<br/>
**1. 调用`search`方法判断映射表的第一个值是否大于100**
```java
@Test
public void searchTest() {
ConcurrentHashMap<Long, Long> map = new ConcurrentHashMap<>();
map.put(new Long(1), 2000L);
map.put(new Long(2), 3000L);
map.put(new Long(3), 1000L);
map.put(new Long(4), 1000L);
//search的第一个参数为阈值
boolean result = map.search(1, (k, v) -> {
//k=1, v=2000
System.out.println("k=" + k + ", v=" + v);
return v <= 1000 ? true : false;
});
//false
System.out.println(result);
}
```
**2. 使用`forEach`方法遍历映射表**
```java
/**
* forEach遍历
*/
@Test
public void forEachTest() {
ConcurrentHashMap<Long, Long> map = new ConcurrentHashMap<>(1);
map.put(new Long(1), 2000L);
map.put(new Long(2), 3000L);
map.put(new Long(3), 1000L);
map.put(new Long(4), 1000L);
map.forEach(1, (k, v) -> System.out.println("Key=" + k + " Value=" + v));
//Key=1 Value=2000
//Key=3 Value=1000
//Key=2 Value=3000
//Key=4 Value=1000
}
/**
* forEach遍历,并过滤
*/
@Test
public void forEachTest2() {
ConcurrentHashMap<Long, Long> map = new ConcurrentHashMap<>(1);
map.put(new Long(1), 2000L);
map.put(new Long(2), 3000L);
map.put(new Long(3), 1000L);
map.put(new Long(4), 1000L);
//只要大于 1000 的元素
map.forEach(1, (k, v) -> v > 1000 ? "Key=" + k + " Value=" + v : null, System.out::println);
//Key=1 Value=2000
//Key=2 Value=3000
}
```
**3. 调用`reduceValues`函数来求映射表中值的总和**
```java
@Test
public void reduceValues() {
ConcurrentHashMap<Long, Long> map = new ConcurrentHashMap<>(1);
map.put(new Long(1), 2000L);
map.put(new Long(2), 3000L);
map.put(new Long(3), 1000L);
map.put(new Long(4), 1000L);
Long sum = map.reduceValues(1, Long::sum);
//7000
System.out.println(sum);
}
```
- 网络通信
- 网络协议
- 端口和套接字
- TCP网络程序
- UDP网络程序
- 多线程聊天室
- 多线程
- 线程相关概念
- 线程实现方式
- 中断线程
- 线程生命周期
- 线程优先级
- 优先级规则
- 案例演示
- 线程同步机制
- 线程同步机制
- synchronized关键字
- ReentrantLock类
- Condition类
- 监视器概念
- volatile关键字
- final变量
- 死锁
- 线程局部变量
- 读/写锁
- 原子类
- 阻塞队列
- 工作规则
- 案例演示
- 常用阻塞队列
- 线程安全集合
- 高效的映射/集/队列
- 并发集视图
- 写数组的拷贝
- Arrays类的并行数组算法
- 同步包装器
- Callable与Future
- 执行器
- 线程池
- 预定执行
- 控制任务组
- Fork-Join框架
- 同步器
- 同步器
- 信号量
- CountDownLatch类
- CyclicBarrier类
- Exchanger类
- SynchronousQueue类
- 线程与Swing
- Swing与线程问题
- 两个原则
- Swing工作线程
- 单一线程规则
- 文件IO
- File类
- 文件输入输出
- ZIP压缩文件
- 集合
- 集合框架
- 集合接口
- 集合实现类
- 线程安全集合
- 集合算法
- 迭代器
- 集合排序
- JDBC
- JDBC是什么
- JDBC-ODBC桥
- JDBC驱动程序类型
- JDBC常用类与接口
- 数据库操作
- 连接数据库
- 增/删/改/查/预处理
- 事务
- 批处理
- commons-dbutils工具
- 安全问题
- Jedis
- 使用Jedis操作Redis数据库
- JSON转换
- 使用连接池
- 案例
- 单例破坏
- 单例定义
- 单例实现方式
- 懒汉式实现单例
- 饿汉式实现单例
- 单例破坏
- 类的单例破坏
- 枚举的单例破坏
- 克隆
- 克隆是什么
- 浅克隆
- 深克隆
- 注解
- 注解是什么
- 三大注解
- 内置注解
- 元注解
- 自定义注解
- NIO
- 相关概念
- BIO/NIO/AIO
- 多线程编程
- 线程同步
- 线程通信
- NIO
- NIO三大核心组件
- NIO网络编程
- NIO文件读写
- AIO
- Java8新特性
- Lambda表达式
- 方法引用
- 函数式接口
- 默认方法
- 什么是默认方法
- 默认方法语法格式
- 多个同名的默认方法问题
- 静态默认方法
- 默认方法实例
- Stream
- Stream是什么
- Stream示例
- Optional容器
- 新的日期时间API
- Base64
- SPI
- SPI是什么
- SPI与API的区别
- 常见场景
- 使用SPI需遵循的约定
- SPI使用步骤