Java并发JUC(java.util.concurrent)集合不安全

简介: Java并发JUC(java.util.concurrent)集合不安全

在这里插入图片描述

👨🏻‍🎓博主介绍:大家好,我是芝士味的椒盐,一名在校大学生,热爱分享知识,很高兴在这里认识大家🌟
🌈擅长领域:Java、大数据、运维、电子
🙏🏻如果本文章各位小伙伴们有帮助的话,🍭关注+👍🏻点赞+🗣评论+📦收藏,相应的有空了我也会回访,互助!!!
🤝另本人水平有限,旨在创作简单易懂的文章,在文章描述时如有错,恳请各位大佬指正,在此感谢!!!

@[TOC]

集合是不安全的

  • 先给大家上个集合框架家族图

在这里插入图片描述

  • List不安全

    package icu.lookyousmileface.notsafe;
    
    import java.util.*;
    import java.util.concurrent.CopyOnWriteArrayList;
    
    /**
     * @author starrysky
     * @title: UnSafeList
     * @projectName Juc_Pro
     * @description: 集合不安全
     * @date 2021/1/2912:04 下午
     */
    public class UnSafeList {
        public static void main(String[] args) {
            // 并发下 ArrayList 不安全的吗,Synchronized;
            /**
             * 解决方案;
             * 1、List<String> list = new Vector<>();
             * 2、List<String> list = Collections.synchronizedList(new ArrayList<>());
             * 3、List<String> list = new CopyOnWriteArrayList<>();
             */
            // CopyOnWrite 写入时复制  COW  计算机程序设计领域的一种优化策略;
            // 多个线程调用的时候,list,读取的时候,固定的,写入(覆盖)
            // 在写入的时候避免覆盖,造成数据问题!
            // 读写分离
            // CopyOnWriteArrayList  比 Vector Nb 在哪里?
    //        List<String> list = new Vector<>();
            List<String> list = new CopyOnWriteArrayList();
    //        List<String> list = Collections.synchronizedList(new ArrayList<>());
    //        List<String> list = new ArrayList<>();
            for (int i = 0; i < 100; i++) {
                new Thread(()->{
                    list.add(UUID.randomUUID().toString().substring(0,5));
                    System.out.println(list);
                },String.valueOf(i)).start();
            }
    
        }
    }
  • Set不安全

    package icu.lookyousmileface.notsafe;
    
    import java.util.Collections;
    import java.util.HashSet;
    import java.util.Set;
    import java.util.UUID;
    import java.util.concurrent.CopyOnWriteArraySet;
    
    /**
     * @author starrysky
     * @title: UnSafeSet
     * @projectName Juc_Pro
     * @description: Set不安全,set底层底层HashMap
     * @date 2021/1/291:00 下午
     */
    public class UnSafeSet {
        public static void main(String[] args) {
            /**
             * 和List同理
             */
    //        Set<String> set = new HashSet<>();
    //        Set<String> set =  Collections.synchronizedSet(new HashSet<>());
            Set<String> set = new CopyOnWriteArraySet<>();
            for (int i = 0; i < 30; i++) {
                new Thread(() -> {
                    set.add(UUID.randomUUID().toString().substring(0, 6));
                    System.out.println(set);
    
                }, String.valueOf(i)).start();
            }
        }
    }

    ⚠️ Tips:

    HashSet底层:底层使用hashmap去重,vlue是常量值,key就是我们传进去的值

    public HashSet() {
            map = new HashMap<>();
        }
    
    public boolean add(E e) {
            return map.put(e, PRESENT)==null;
        }
    
    private static final Object PRESENT = new Object();
  • Map不安全

    package icu.lookyousmileface.notsafe;
    
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.UUID;
    import java.util.concurrent.ConcurrentHashMap;
    
    /**
     * @author starrysky
     * @title: UnSafeMap
     * @projectName Juc_Pro
     * @description: Map不安全
     * @date 2021/1/291:14 下午
     */
    public class UnSafeMap {
        public static void main(String[] args) {
            // map 是这样用的吗? 不是,工作中不用 HashMap
            // 默认等价于什么?  new HashMap<>(16,0.75);
    //        Map<String,String> map = new HashMap<>();
    //        Map<String,String> map = Collections.synchronizedMap(new HashMap<>());
            Map<String,String> map =  new ConcurrentHashMap<>();
            for (int i = 0; i < 30; i++) {
                new Thread(()->{
                    map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
                    System.out.println(map);
                },String.valueOf(i)).start();
            }
        }
    }

    ⚠️ Tips:new HashMap<>();默认等价于new HashMap<>(16,0.75);,工作中不用 HashMap

Callable

在这里插入图片描述
在这里插入图片描述

  • 代码实现:

    package icu.lookyousmileface.callable;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    /**
     * @author starrysky
     * @title: CallableUse
     * @projectName Juc_Pro
     * @description: Callable代替了Runable接口,run/call,有异常有返回值
     * @date 2021/1/291:43 下午
     */
    public class CallableUse {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            //适配类
            FutureTask<Integer> integerFutureTask = new FutureTask<>(new MyThread());
            new Thread(integerFutureTask,"A").start();
            new Thread(integerFutureTask,"B").start();//有缓存,效率高
            //有返回值,get会产生阻塞,可以使用异步线程通信长处理
            System.out.println(integerFutureTask.get());
        }
    }
    
    class MyThread implements Callable<Integer> {
    
        @Override
        public Integer call() throws Exception {
            System.out.println("调用了call方法");
            return 1024;
        }
    }

    ⚠️ Tips:

    1、可以有返回值

    2、可以抛出异常

    3、方法不同,run()/ call()

    细节:

    1、有缓存

    2、结果可能需要等待,会阻塞!

常用辅助类(必会)

  • CountDownLatch

    package icu.lookyousmileface.concurrentutils;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.FutureTask;
    
    /**
     * @author starrysky
     * @title: CountDownLatchUse
     * @projectName Juc_Pro
     * @description: CountDownLatch辅助类常用必会
     * @date 2021/1/292:13 下午
     */
    public class CountDownLatchUse {
        public static void main(String[] args) {
            CountDownLatch countDownLatch = new CountDownLatch(6);
    
            for (int i = 1; i <=6; i++) {
                new Thread(new FutureTask<Integer>(()-> {
                    System.out.println(Thread.currentThread().getName()+":"+"走出教室!");
                    countDownLatch.countDown();
                    return 1024;}),String.valueOf(i)).start();
            }
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("全员已经都离开来教室,教室大门锁上!!!");
        }
    }

    ⚠️ 原理:countDownLatch.countDown(); // 数量-1,countDownLatch.await(); // 等待计数器归零,然后再向下执行,每次有线程调用 countDown() 数量-1,假设计数器变为0,countDownLatch.await() 就会被唤醒,继续执行!

  • CyclicBarrier

    package icu.lookyousmileface.concurrentutils;
    
    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    
    /**
     * @author starrysky
     * @title: CyclicBarrierUse
     * @projectName Juc_Pro
     * @description: 加法计数器
     * @date 2021/1/292:28 下午
     */
    public class CyclicBarrierUse {
        public static void main(String[] args) {
    //CyclicBarrier本身就可以自带一个线程
            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 {
                        //此处的await是等待达到parties计数的时候,执行cyclicBarrier的线程,没有将不会执行
                        cyclicBarrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                },String.valueOf(i)).start();
            }
        }
    }
  • Semaphore

    • 信号量,也就是限流的作用,比较常用
    package icu.lookyousmileface.concurrentutils;
    
    import java.util.concurrent.Semaphore;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author starrysky
     * @title: SemaphoreUse
     * @projectName Juc_Pro
     * @description:
     * @date 2021/1/292:38 下午
     */
    public class SemaphoreUse {
        public static void main(String[] args) {
            //可以用来限制流量
            Semaphore semaphore = new Semaphore(3);
    
            for (int i = 1; i <= 6; i++) {
                new Thread(()->{
                    try {
                        //关闸
                        semaphore.acquire();
                        System.out.println(Thread.currentThread().getName()+"入库");
                        TimeUnit.SECONDS.sleep(2);
                        System.out.println(Thread.currentThread().getName()+"离开");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally {
                        //开闸
                        semaphore.release();
                    }
                },String.valueOf(i)).start();
            }
        }
    }

    ⚠️ Tips:

    原理:

    semaphore.acquire() 获得,假设如果已经满了,等待,等待被释放为止!semaphore.release(); 释放,会将当前的信号量释放 + 1,然后唤醒等待的线程!

    作用: 多个共享资源互斥的使用!并发限流,控制最大的线程数!

读写锁

  • 试验代码:

    package icu.lookyousmileface.readeandwritelock;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    /**
     * @author starrysky
     * @title: ReadAndWriteLockUse
     * @projectName Juc_Pro
     * @description:  读写锁
     * 独占锁(写锁) 一次只能被一个线程占有
     * 共享锁(读锁) 多个线程可以同时占有
     * ReadWriteLock
     * 读-读  可以共存!
     * 读-写  不能共存!
     * 写-写  不能共存!
     * @date 2021/1/293:08 下午
     */
    public class ReadAndWriteLockUse {
        public static void main(String[] args) {
            MyCache myCache = new MyCache();
    
            for (int i = 1; i <= 6; i++) {
                final int  temp = i;
                new Thread(()->{
                    myCache.put(String.valueOf(temp),String.valueOf(temp));
                },String.valueOf(i)).start();
            }
    
            for (int i = 1; i <= 6; i++) {
                final int  temp = i;
                new Thread(()->{
                    myCache.get(String.valueOf(temp));
                },String.valueOf(i)).start();
            }
        }
    }
    
    class MyCache{
    
        private final Map<String,String> map = new HashMap<>(16, 0.75F);
        private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    
        // 存,写入的时候,只希望同时只有一个线程写
        public void put(String key,String value){
            reentrantReadWriteLock.writeLock().lock();
            try {
                System.out.println(Thread.currentThread().getName()+":正在插入"+key);
                map.put(key, value);
                System.out.println(Thread.currentThread().getName()+":"+key+"插入完成!");
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                reentrantReadWriteLock.writeLock().unlock();
            }
        }
        // 取,读,所有人都可以读!
        public void get(String key){
            reentrantReadWriteLock.readLock().lock();
            try{
                System.out.println(Thread.currentThread().getName()+":正在读取"+key);
                String value = map.get(key);
                System.out.println(Thread.currentThread().getName()+":"+key+"读取完成!"+"值:"+value);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                reentrantReadWriteLock.readLock().unlock();
            }
        }
    }

阻塞队列

在这里插入图片描述

  • 阻塞队列:
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

⚠️ Tips:会使用 阻塞队列的地方:多线程并发处理,线程池!

四组API

在这里插入图片描述

SynchronousQueue 同步队列

  • 试验代码:

    package icu.lookyousmileface.syncqueue;
    
    import java.util.concurrent.SynchronousQueue;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author starrysky
     * @title: SyncQueueUse
     * @projectName Juc_Pro
     * @description: 同步队列
     * @date 2021/1/299:17 下午
     */
    public class SyncQueueUse {
        public static void main(String[] args) throws InterruptedException {
            SynchronousQueue<String> syncQueue = new SynchronousQueue<>();
    
            //无须for循环会自动产生一种互喂模式
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + ":" + "A入队列");
                    syncQueue.put("A");
    
                    System.out.println(Thread.currentThread().getName() + ":" + "B入队列");
                    syncQueue.put("B");
    
                    System.out.println(Thread.currentThread().getName() + ":" + "C入队列");
                    syncQueue.put("C");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "T1").start();
    
            new Thread(() -> {
                try {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println(Thread.currentThread().getName() + ":" + syncQueue.take() + "出队列");
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println(Thread.currentThread().getName() + ":" + syncQueue.take() + "出队列");
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println(Thread.currentThread().getName() + ":" + syncQueue.take() + "出队列");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "T2").start();
    
        }
    }

    ⚠️ Tips:

    没有容量,进去一个元素,必须等待取出来之后,才能再往里面放一个元素!put、take

相关文章
|
6月前
|
Java 大数据 API
Java Stream API:现代集合处理与函数式编程
Java Stream API:现代集合处理与函数式编程
339 100
|
6月前
|
Java API 数据处理
Java Stream API:现代集合处理新方式
Java Stream API:现代集合处理新方式
352 101
|
5月前
|
Java 大数据 Go
从混沌到秩序:Java共享内存模型如何通过显式约束驯服并发?
并发编程旨在混乱中建立秩序。本文对比Java共享内存模型与Golang消息传递模型,剖析显式同步与隐式因果的哲学差异,揭示happens-before等机制如何保障内存可见性与数据一致性,展现两大范式的深层分野。(238字)
153 4
|
前端开发 Java C++
JUC系列之《CompletableFuture:Java异步编程的终极武器》
本文深入解析Java 8引入的CompletableFuture,对比传统Future的局限,详解其非阻塞回调、链式编排、多任务组合及异常处理等核心功能,结合实战示例展示异步编程的最佳实践,助你构建高效、响应式的Java应用。
|
5月前
|
设计模式 算法 安全
JUC系列之《深入理解AQS:Java并发锁的基石与灵魂 》
本文深入解析Java并发核心组件AQS(AbstractQueuedSynchronizer),从其设计动机、核心思想到源码实现,系统阐述了AQS如何通过state状态、CLH队列和模板方法模式构建通用同步框架,并结合独占与共享模式分析典型应用,最后通过自定义锁的实战案例,帮助读者掌握其原理与最佳实践。
|
5月前
|
缓存 安全 Java
JUC系列《深入浅出Java并发容器:CopyOnWriteArrayList全解析》
CopyOnWriteArrayList是Java中基于“写时复制”实现的线程安全List,读操作无锁、性能高,适合读多写少场景,如配置管理、事件监听器等,但频繁写入时因复制开销大需谨慎使用。
|
5月前
|
缓存 安全 Java
如何理解Java中的并发?
Java并发指多任务交替执行,提升资源利用率与响应速度。通过线程实现,涉及线程安全、可见性、原子性等问题,需用synchronized、volatile、线程池及并发工具类解决,是高并发系统开发的关键基础。(238字)
321 5
|
5月前
|
存储 算法 安全
Java集合框架:理解类型多样性与限制
总之,在 Java 题材中正确地应对多样化与约束条件要求开发人员深入理解面向对象原则、范式编程思想以及JVM工作机理等核心知识点。通过精心设计与周密规划能够有效地利用 Java 高级特征打造出既健壮又灵活易维护系统软件产品。
158 7
|
10月前
|
机器学习/深度学习 消息中间件 存储
【高薪程序员必看】万字长文拆解Java并发编程!(9-2):并发工具-线程池
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的强力并发工具-线程池,废话不多说让我们直接开始。
367 0
|
安全 Java 调度
解锁Java并发编程高阶技能:深入剖析无锁CAS机制、揭秘魔法类Unsafe、精通原子包Atomic,打造高效并发应用
【8月更文挑战第4天】在Java并发编程中,无锁编程以高性能和低延迟应对高并发挑战。核心在于无锁CAS(Compare-And-Swap)机制,它基于硬件支持,确保原子性更新;Unsafe类提供底层内存操作,实现CAS;原子包java.util.concurrent.atomic封装了CAS操作,简化并发编程。通过`AtomicInteger`示例,展现了线程安全的自增操作,突显了这些技术在构建高效并发程序中的关键作用。
248 1