目录
1.多线程下线程不安全的问题
1.使用多个线程对Array List集合进行添加操作并打印,查看结果
2.如何在多线程环境下使用线程安全的集合类
CopyOnWriteArrayList
3.多线程环境下使用队列
4.多线程环境下使用哈希表
1.HashTable线程安全
2.Concurrent Hash Map线程安全
1.更小的锁粒度(加锁范围)
2.只给写加锁,不给读加锁
3.充分利用CAS机制
4.对扩容进行了特殊优化
5.死锁
1.产生方式
2.产生原因
3.避免死锁
1.循环等待
2.银行家算法
1.多线程下线程不安全的问题
1.使用多个线程对Array List集合进行添加操作并打印,查看结果
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
int finalI = i+1;
Thread thread = new Thread(() -> {
list.add(finalI);
System.out.println(list);
});
thread.start();
}
}
出现了并发修改异常
2.如何在多线程环境下使用线程安全的集合类
- 使用Vector,Hash Table,JDK中提供的线程安全的类(强烈不推荐)
- 自己使用同步机制Synchronized或者Reentrant Lock(和上面效果一样,也不推荐)
- 使用工具类转换 Collections.synchronizedList(new ArrayList) (上面三个实现的原理基本一样,都不推荐)
CopyOnWriteArrayList
他是JUC包下的一个类,使用的是一种叫写时复制技术来实现的
- 当要修改一个集合时,先复制这个集合的副本
- 修改副本的数据,修改完成后,用副本覆盖原始集合
优点:在读多写少的场景下,性能很高,不需要加锁竞争
缺点:占用内存较多,新写的数据不能被第一时间读取到
不会在多线程情况下产生异常
3.多线程环境下使用队列
4.多线程环境下使用哈希表
Hash Map本身是线程不安全的类,正常单线程使用没有问题,由于没有加锁,在多线程环境下会产生线程安全的问题
1.HashTable线程安全
实现方法就是通过Synchronized给自己加锁,读写的时候都会加锁,这样效率太低,不建议使用
2.Concurrent Hash Map线程安全
多线程环境下强烈推荐使用这种方式保证线程安全,他与Hash Table,Collections不同,并不是使用synchronized关键字实现加锁的,而是通过JUC包下的Reentrant Lock实现加锁
1.更小的锁粒度(加锁范围)
Hash Table对所有操作全部加锁,必然会对性能有影响
Concurrent Hash Map对每个Hash桶进行加锁,提高并发能力
2.只给写加锁,不给读加锁
加锁的方式是Reentrant Lock,大量运用CAS操作,而且共享变量使用volatile修饰
3.充分利用CAS机制
4.对扩容进行了特殊优化
5.死锁
1.产生方式
例如两个线程两把锁
就会产生死锁
2.产生原因
- 互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
- 不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
- 请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
- 循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样
- 就形成了一个等待环路。
3.避免死锁
- 当上述四个条件都成立的时候,便形成死锁。当然,死锁的情况下如果打破上述任何一个条件,便可让死锁消失。
- 其中互斥使用和不可抢占是锁的基本特性,不能打破
- 请求保持有可能打破,这取决于代码如何写
- 然而最容易破坏的还是 "循环等待"
1.循环等待
2.银行家算法
Thread Local 将所有的资源进行统一分配
例如:
public class Demo05 {
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
Thread thread = new Thread(() -> {
int count = 10;
threadLocal.set(count);
print();
},"class1");
Thread thread1 = new Thread(() -> {
int count = 20;
threadLocal.set(count);
print();
},"class2");
thread1.start();
thread.start();
}
public static void print() {
Integer count = threadLocal.get();
System.out.println(Thread.currentThread().getName()+"定制校服"+count);
}
}