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

相关文章
|
15天前
|
存储 缓存 安全
Java 集合江湖:底层数据结构的大揭秘!
小米是一位热爱技术分享的程序员,本文详细解析了Java面试中常见的List、Set、Map的区别。不仅介绍了它们的基本特性和实现类,还深入探讨了各自的使用场景和面试技巧,帮助读者更好地理解和应对相关问题。
36 5
|
28天前
|
存储 缓存 安全
Java 集合框架优化:从基础到高级应用
《Java集合框架优化:从基础到高级应用》深入解析Java集合框架的核心原理与优化技巧,涵盖列表、集合、映射等常用数据结构,结合实际案例,指导开发者高效使用和优化Java集合。
39 4
|
1月前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
1月前
|
Java
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式。本文介绍了 Streams 的基本概念和使用方法,包括创建 Streams、中间操作和终端操作,并通过多个案例详细解析了过滤、映射、归并、排序、分组和并行处理等操作,帮助读者更好地理解和掌握这一重要特性。
35 2
|
1月前
|
存储 Java
判断一个元素是否在 Java 中的 Set 集合中
【10月更文挑战第30天】使用`contains()`方法可以方便快捷地判断一个元素是否在Java中的`Set`集合中,但对于自定义对象,需要注意重写`equals()`方法以确保正确的判断结果,同时根据具体的性能需求选择合适的`Set`实现类。
|
1月前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
1月前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
1月前
|
存储 Java 开发者
在 Java 中,如何遍历一个 Set 集合?
【10月更文挑战第30天】开发者可以根据具体的需求和代码风格选择合适的遍历方式。增强for循环简洁直观,适用于大多数简单的遍历场景;迭代器则更加灵活,可在遍历过程中进行更多复杂的操作;而Lambda表达式和`forEach`方法则提供了一种更简洁的函数式编程风格的遍历方式。
|
1月前
|
Java 开发者
|
1月前
|
存储 Java 开发者
Java中的集合框架深入解析
【10月更文挑战第32天】本文旨在为读者揭开Java集合框架的神秘面纱,通过深入浅出的方式介绍其内部结构与运作机制。我们将从集合框架的设计哲学出发,探讨其如何影响我们的编程实践,并配以代码示例,展示如何在真实场景中应用这些知识。无论你是Java新手还是资深开发者,这篇文章都将为你提供新的视角和实用技巧。
34 0