跟南桑学JUC第三节课(CopyOnWriteArrayList,CopyOnwriteArratSet,ConcurrentHashMap,走进Callable)

11.CopyOnWriteArrayList

public class ListTest {
    public static void main(String[] args) {
        //并发下ArrayList不安全
        /**
         * 解决方法:
         * 1. List<Integer> list = new Vector<>();
         * 2. List<Integer> list = Collections.synchronizedList(new ArrayList<>());
         * 3. List<Integer> list = new CopyOnWriteArrayList<>()
         */
        //CopyOnWrite 写入时复制 COW 计算机程序设计领域的一种优化策略
        //多个线程调用的时候,list,读取的时候,固定的,写入(覆盖)
        //在写入的时候避免覆盖,造成数据问题
        //读写分离
        //CopyOnWriteArrayList 比 Vector 的优势
        List<Integer> list = new CopyOnWriteArrayList<>();
        for (int i = 1; i <= 1000; i++) {
            final int finalI = i;
            new Thread(() -> {
                list.add(finalI);
            }, String.valueOf(i)).start();
        }
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

源码比较

CopyOnWriteArrayList的add源码

public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}

Vector的add源码

public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}

学习方法推荐:1.先会用,2.寻找其他解决方案,3.分析源码

12.CopyOnWriteArraySet

public class SetTest {
    public static void main(String[] args) {
        /**
         * Set<Integer> set = new HashSet<>(); 不安全
         * 解决方案:
         * 1. Set<Integer> set = Collections.synchronizedSet(new HashSet<>());
         * 2. Set<Integer> set = new CopyOnWriteArraySet<>();
         */
        Set<Integer> set = new CopyOnWriteArraySet<>();
        for (int i = 1; i <= 1000; i++) {
            final int finalI = i;
            new Thread(() -> {
                set.add(finalI);
            }).start();
        }

        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(set.size());
    }
}

HashSet底层是什么?入参构造方法和add方法如下(源码)

public HashSet() {
    map = new HashMap<>();
}
// 常量
private static final Object PRESENT = new Object();
//add set本质就是map key是无法重复的!
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

13.ConcurrentHashMap

public class MapTest {
    public static void main(String[] args) {
        //new HashMap<>() 等价于 new HashMap<>(16, 0.75f)
        /**
         * Map<Integer, String> map = new HashMap<>(); 不安全
         * 解决方案:
         * 1. Map<Integer, String> map = Collections.synchronizedMap(new HashMap<>());
         * 2. Map<Integer, String> map = new ConcurrentHashMap<>();
         */
        //看看官方api文档
        Map<Integer, String> map = new ConcurrentHashMap<>();

        for (int i = 1; i <= 1000; i++) {
            final int finalI = i;
            new Thread(() -> {
                map.put(finalI, Thread.currentThread().getName());
            }, String.valueOf(i)).start();
        }

        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(map.size());

    }
}

14.走进Callable

image-20210412215017115

  1. 可以有返回值
  2. 可以抛出异常
  3. 方法不同,call()

Runnable的Api文档

image-20210412215902849

FutureTask的Api文档

image-20210412220923181

image-20210412220818395

public class CallableTest {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        //Callable  ---   Runnable  中间转换(适配类)
        FutureTask<String> futureTask = new FutureTask<>(thread);
        //结果会被缓存,提高效率
        new Thread(futureTask, "A").start();
        new Thread(futureTask, "B").start();

        //获取返回值
        try {
            //这个get方法可能会产生阻塞,把它放到最后
            String s = futureTask.get();
            //或者使用异步通信来处理
            System.out.println(s);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
}

class MyThread implements Callable<String> {
    @Override
    public String call() {
        System.out.println("call()");
        //可能是耗时的操作
        return "123";
    }
}

输出

call()
123

细节;

  1. 有缓存
  2. 结果可能需要等待,会阻塞!

15.CountDownLatch

image-20210412222203712

减法计数器

public class CountDownLatchDemo {
    public static void main(String[] args) {
        //总数是6 必须要执行的任务的时候再使用
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName()+" go out");
                // 数量-1
                countDownLatch.countDown();
            }).start();
        }

        try {
            //等待计数器归零,然后再往下执行
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("close door");

    }
}

原理:

countDownLatch.countDown(); //数量-1

countDownLatch.await();//等待计数器归零,然后再往下执行

每次线程调用countDown()数量-1,假设计数器变成0,countDownLatch.await()就会被唤醒,继续执行!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值