1 集合类不安全
1.1 CopyOnWriteArrayList
📢 并发下ArrayList是不安全的,可能会引发java.util.ConcurrentModificationException并发修改异常!
📌 解决方案
List<String> list = new Vector<>();(JDK1.0引入,使用synchronized加锁)
List<String> list = Collections.synchronizedList(new ArrayList<>());
List<String> list = new CopyOnWriteArrayList<>();
📌 要点
CopyOnWrite写入时复制,读写分离,COW时计算机程序设计领域的一种优化策略;
普通list写入时是覆盖之前的旧数据,那么高并发情况下,写入可能会造成数据问题;
CopyOnWriteArrayList相比Vector效率高一些(高并发环境下lock锁相比synchronized效率高)
📌 缺点
无法保证数据实时一致性,当执行add或remove操作没完成时,get获取的仍然获取旧数组
会消耗大量内存
📌 代码参考
public class ListTest {
public static void main(String[] args) {
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 1; i <= 10; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
1.2 CopyOnWriteArraySet
📢 HashSet类似ArrayList,会引发java.util.ConcurrentModificationException并发修改异常!
📌 解决方案
Set<String> set = Collections.synchronizedSet(new HashSet<>());
Set<String> set = new CopyOnWriteArraySet<>();
📌 HashSet底层是什么?
//1.直接new了个HashMap
public HashSet() {
map = new HashMap<>();
}
//2.set中的值实际上是HashMap中的key,因为HashMap中key不重复
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
//3.PRESENT是常量,固定值
private static final Object PRESENT = new Object();
📌 代码参考
public class SetTest {
public static void main(String[] args) {
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 1; i <= 10; i++) {
new Thread(()->{
set.add(Thread.currentThread().getName());
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
1.3 ConcurrentHashMap
📢 HashMap类似ArrayList,会引发java.util.ConcurrentModificationException并发修改异常!
📌 解决方案
Map<String,Integer> map = Collections.synchronizedMap(new HashMap<>());
Map<String,Integer> map = new ConcurrentHashMap<>();
📌 注意
ConcurrentHashMap和list、set的名称不大一样;
new HashMap<>()默认等于new HashMap<>(16,0.75)
代码参考
public class MapTest {
public static void main(String[] args) {
Map<String,Integer> map = new ConcurrentHashMap<>();
for (int i = 0; i < 10; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(),0);
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
2 常用辅助类
指的是juc里边的常用辅助类
2.1 CountDownLatch
📌 减法计数器
每次有线程调用countDown()数量-1;
假设计数器变为0,countDownLatch.await()就会被唤醒,继续执行。
//计数器
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
//总数是6
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i < 6; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+" go out");
countDownLatch.countDown();
},String.valueOf(i)).start();
}
countDownLatch.await();//等待计数器归零,然后再向下执行
System.out.println("Over");
}
}
2.2 CyclicBarrier
📌 加法计数器
cyclicBarrier.await()相当于一个印记,放在某个线程下,该线程启动后会默认加一;
等线程数量到达指定数后,就会启动"召唤神龙成功!"此线程。
public class CyclicBarrierDemo {
public static void main(String[] args){
//集齐7颗龙珠召唤神龙
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
System.out.println("召唤神龙成功!");
});
for (int i = 1; i <= 7; i++) {
final int temp = i;
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"收集第"+temp+"个龙珠");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
2.3 Semaphore
📌 信号量
semaphore.acquire();获得,如果满了会等到其释放为止;
semaphore.release();释放,会将当前的信号量+1,然后唤醒等待的线程。
作用:多个共享资源互斥的使用!开发限流,控制最大的线程数。
public class SemaphoreDemo {
public static void main(String[] args) {
// 线程数量:停车位
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 6; i++) {
new Thread(()->{
try {
semaphore.acquire();//得到
System.out.println(Thread.currentThread().getName()+"抢到车位");
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName()+"离开车位");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();//释放
}
},String.valueOf(i)).start();
}
}
}