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

并发容器类和辅助类

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

  • JDK1.8之前 采用“锁分段机制”

  • JDK1.8之后 采用 synchronized 锁住Node节点,结合CAS和volatile实现

除此之外,还有:

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

并发容器类的使用

来看下面一段代码

public class TestCopyOnWriteArraySet {

    public static void main(String[] args)
    {
        HelloThread ht = new HelloThread();
        for (int i=0;i<10;i++)
        {
            new Thread(ht).start();
        }
    }

}
class HelloThread implements  Runnable{

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

    static {
        list.add("A");
        list.add("B");
        list.add("C");

    }
    @Override
    public void run() {
        Iterator<String> it = list.iterator();

        while (it.hasNext()){
            System.out.println(it.next());
            list.add("A");
        }
    }
}
代码逻辑:
  • HelloThread线程中有个list,初始数据为 A B C
  • run方法里打印下一个对象并添加新对象
  • 还是在主方法里用十个线程同时进行上述操作

实际上执行结果:

抛出ConcurrentModificationException异常

发生了什么?

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

怎么解决?

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

把之前的 ArrayList替换为 CopyOnWriteArrayList

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

程序可以正常执行

CopyOnWriteArrayList

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

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

CountDownLatch闭锁

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

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

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

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

看下面的实现代码:

import java.util.concurrent.CountDownLatch;

public class TestCountDownLatch {
    public static void main(String[] args) throws InterruptedException {
        //初始值为5,每次有线程完成就递减1,直到0才可以继续执行
        CountDownLatch latch = new CountDownLatch(5);
        LatchDemo latchDemo =new LatchDemo(latch);
		
        //10个线程没启动时的时间
        long start = System.currentTimeMillis();
        for (int i=0;i<10;i++)
        {
            new Thread(latchDemo).start();
        }
		//等待latch闭锁
        latch.await();
        //闭锁中count减到0了,记录现在的时间
        long end = System.currentTimeMillis();
        System.out.println("耗费时间:"+(end-start));
    }
}
class LatchDemo implements Runnable{

    private CountDownLatch latch;
	
    //构造函数
    public LatchDemo(CountDownLatch latch)
    {
        this.latch = latch;
    }
    @Override
    public void run() {
        //避免冲突
        synchronized (this)
        {
            try{
                //打印1-50000之间的偶数
                for(int i = 0;i<50000;i++)
                {
                    if(i%2==0)
                    {
                        System.out.println(i);
                    }
                }
            }
            //无论如何,此方法最后都把闭锁的count减一
            finally {
                latch.countDown();
            }
        }
    }
}

实际执行结果:

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

原创文章 40 获赞 16 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_43925277/article/details/105123835
今日推荐