Java并发编程(三):并发容器类和辅助类

矫情吗;* 2023-07-18 09:48 24阅读 0赞

并发容器类和辅助类

Java提供了很多支持并发的容器类,例如我们比较熟悉的用来在并发场景下代替HashMapConcurrentHashMap类:

  • JDK1.8之前 采用“锁分段机制”
  • JDK1.8之后 采用 synchronized 锁住Node节点,结合CAS和volatile实现

除此之外,还有:

  • ConcurrentSkipListMap通常优于同步的TreeMap
  • 在应对并发场景下多次读取和遍历操作时的ArrayList代替者CopyOnWriteArrayList等等

并发容器类的使用

来看下面一段代码

  1. public class TestCopyOnWriteArraySet {
  2. public static void main(String[] args)
  3. {
  4. HelloThread ht = new HelloThread();
  5. for (int i=0;i<10;i++)
  6. {
  7. new Thread(ht).start();
  8. }
  9. }
  10. }
  11. class HelloThread implements Runnable{
  12. private static List<String> list = Collections.synchronizedList(new ArrayList<>());
  13. static {
  14. list.add("A");
  15. list.add("B");
  16. list.add("C");
  17. }
  18. @Override
  19. public void run() {
  20. Iterator<String> it = list.iterator();
  21. while (it.hasNext()){
  22. System.out.println(it.next());
  23. list.add("A");
  24. }
  25. }
  26. }
代码逻辑:
  • HelloThread线程中有个list,初始数据为 A B C
  • run方法里打印下一个对象并添加新对象
  • 还是在主方法里用十个线程同时进行上述操作

实际上执行结果:

抛出ConcurrentModificationException异常

发生了什么?

发生了并发修改异常,迭代操作同一个数据源

怎么解决?

使用Java提供的 CopyOnWriteArrayList容器类即可

把之前的 ArrayList替换为 CopyOnWriteArrayList

  1. // private static List<String> list = Collections.synchronizedList(new ArrayList<>());
  2. private static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

程序可以正常执行

CopyOnWriteArrayList

每次写入时,都会在底层复制新列表,然后再添加,避免了并发修改异常

所以比较适合迭代多的场景,不适合添加操作过多的场景

CountDownLatch闭锁

是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待

  • 目标:只有当其他所有线程的运算全部完成,当前运算才继续执行

可以使用闭锁这个性质,来计算所有线程运行完毕究竟耗费了多少时间

把主线程执行的逻辑放到所有线程执行完毕后再执行

看下面的实现代码:

  1. import java.util.concurrent.CountDownLatch;
  2. public class TestCountDownLatch {
  3. public static void main(String[] args) throws InterruptedException {
  4. //初始值为5,每次有线程完成就递减1,直到0才可以继续执行
  5. CountDownLatch latch = new CountDownLatch(5);
  6. LatchDemo latchDemo =new LatchDemo(latch);
  7. //10个线程没启动时的时间
  8. long start = System.currentTimeMillis();
  9. for (int i=0;i<10;i++)
  10. {
  11. new Thread(latchDemo).start();
  12. }
  13. //等待latch闭锁
  14. latch.await();
  15. //闭锁中count减到0了,记录现在的时间
  16. long end = System.currentTimeMillis();
  17. System.out.println("耗费时间:"+(end-start));
  18. }
  19. }
  20. class LatchDemo implements Runnable{
  21. private CountDownLatch latch;
  22. //构造函数
  23. public LatchDemo(CountDownLatch latch)
  24. {
  25. this.latch = latch;
  26. }
  27. @Override
  28. public void run() {
  29. //避免冲突
  30. synchronized (this)
  31. {
  32. try{
  33. //打印1-50000之间的偶数
  34. for(int i = 0;i<50000;i++)
  35. {
  36. if(i%2==0)
  37. {
  38. System.out.println(i);
  39. }
  40. }
  41. }
  42. //无论如何,此方法最后都把闭锁的count减一
  43. finally {
  44. latch.countDown();
  45. }
  46. }
  47. }
  48. }

实际执行结果:

这时候算出的时间就是等待所有线程运行完之后花费的时间

发表评论

表情:
评论列表 (有 0 条评论,24人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java并发编程---同步容器

    一.概念 同步类容器都是线程安全的,但在某些场景下可能需要加锁来保护复合操作.复合类操作如:迭代(反复访问元素,遍历完容器中所有的元素),跳转(根据指定的顺序找到当前元素的下