JUC并发编程——集合类不安全及Callable(基于狂神说的学习笔记)

这篇具有很好参考价值的文章主要介绍了JUC并发编程——集合类不安全及Callable(基于狂神说的学习笔记)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

集合类不安全

List不安全

package unsafe;

import PC.A;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

// ArrayList线程不安全,在多线程下使用ArrayList会报错:
// java.util.ConcurrentModificationException 并发修改异常
public class ListTest {

    public static void main(String[] args) {
        // 并发下ArrayList 不安全
        /**
         * 解决方案:
         * 1、List<String> list= new Vector<>();
         * 实际上,Vector在jdk1.0就已经出来了,ArrayList在jdk1.2才出来
         * Vector实际上就是在add时将方法synchronized锁起来了
         * 2、List<String> list = Collections.synchronizedList(new ArrayList<>());// 通过工具类将其转换乘synchronized保证线程安全
         * 3、List<String> list = new CopyOnWriteArrayList<>();
         */
        //List<String> list = new ArrayList<>();
        //List<String> list = Collections.synchronizedList(new ArrayList<>());
        List<String> list = new CopyOnWriteArrayList<>();
        // CopyOnWrite 写入时复制 COW 计算机程序设计领域的一种优化策略
        // 多个线程调用的时候 list,读取的时候,固定的,写入的时候可能会覆盖
        // 在写入的时候避免覆盖,造成数据问题
        // CopyOnWriteArrayList比Vector的优势在于,copyOnWriteArrayList在并发下效率要比vector更高,因为CopyOnWriteArrayList使用的是写时复制策略,而非使用synchronized锁
        for (int i = 1; i <= 10; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

CopyOnWriteArrayList与vector对比,以下来自CSDN智能助手的回答:

Java中的CopyOnWriteArrayList和Vector都是线程安全的动态数组,可以在多线程环境下使用。

CopyOnWriteArrayList使用了一种特殊的写时复制机制,它在对数组进行修改时,会创建一个新的副本,而不是直接在原数组上进行修改。这样可以确保在多线程并发修改时不会发生问题。然而,由于每次修改都需要创建一个新的副本,所以在写入操作频繁的情况下,CopyOnWriteArrayList的性能可能会比较低。

Vector是一个同步的动态数组,它使用了synchronized关键字来保证多线程安全。当多个线程同时修改Vector时,每个线程会依次获得锁,并按顺序执行相应的修改操作。虽然Vector具有线程安全性,但在高并发情况下,由于需要频繁的获取锁和释放锁,可能会导致性能下降。

因此,如果在多线程环境下需要频繁进行读操作,而写操作较少的情况下,CopyOnWriteArrayList可能会更适合。而如果需要频繁进行写操作或者对线程安全性要求不是非常高,可以考虑使用Vector。

而后笔者在IDEA中翻阅关于CopyOnWriteArralyList的add()方法源码发现,它还是使用synchronized关键字,源码如下:

/**
 * Appends the specified element to the end of this list.
 *
 * @param e element to be appended to this list
 * @return {@code true} (as specified by {@link Collection#add})
 */
public boolean add(E e) {
    synchronized (lock) {
        Object[] es = getArray();
        int len = es.length;
        es = Arrays.copyOf(es, len + 1);
        es[len] = e;
        setArray(es);
        return true;
    }
}

然后笔者去查了文心一言的回答,得到的答案依然是CopyOnWriteArralyList没有使用synchronized锁,回答如下:

CopyOnWriteArrayListadd()方法不是使用synchronized关键字来实现的。实际上,CopyOnWriteArrayListadd()方法内部使用的是一种称为"写时复制"(Copy-on-Write)的技术。

在写时复制策略中,数据结构被修改时,修改操作会在一个新的副本上进行,而不是直接在原始数据上进行。这样,读取操作可以在原始数据上进行,而不需要等待写入操作完成。写入操作完成后,原始数据会被丢弃,新的副本会成为新的原始数据。

CopyOnWriteArrayList中,当调用add()方法时,它会创建一个新的数组副本,将新元素添加到该副本中,然后将原始数组引用指向新数组。由于创建新数组和修改操作是在一个单独的写锁上进行的,因此多个线程可以同时读取原始数组,而不会看到正在进行的写入操作。

因此,CopyOnWriteArrayListadd()方法并没有使用synchronized关键字来实现线程安全。而是使用写时复制技术和读写锁来实现更细粒度的并发控制。

因此,copyWriteArrayList在并发下是否效率更高有待商榷

Set不安全

package unsafe;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;

public class SetTest {

    public static void main(String[] args) {

       // Set<String> set = new HashSet<>(); // 线程不安全
       // Set<String> set = Collections.synchronizedSet(new HashSet<>());// 通过工具类将其转换成synchronized保证线程安全
        Set<String> set = new CopyOnWriteArraySet<>();// 通过读写复制策略保证线程安全

        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(set);
            },String.valueOf(i)).start();

        }
    }
}

hashSet的底层是什么?

HashSet本质上就是HashMap,源码:

/**
 * Constructs a new, empty set; the backing {@code HashMap} instance has
 * default initial capacity (16) and load factor (0.75).
 */
public HashSet() {
    map = new HashMap<>();
}

// add() set本质就是map key是无法重复的
    /**
     * Adds the specified element to this set if it is not already present.
     * More formally, adds the specified element {@code e} to this set if
     * this set contains no element {@code e2} such that
     * {@code Objects.equals(e, e2)}.
     * If this set already contains the element, the call leaves the set
     * unchanged and returns {@code false}.
     *
     * @param e element to be added to this set
     * @return {@code true} if this set did not already contain the specified
     * element
     */
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
// PRESENT 是一个常量
    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();

HashMap不安全

ConcurrentHashMap<>()

package unsafe;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class MapTest {

    public static void main(String[] args) {
        // 线程不安全
        // map 是下面这条语句这样用的码?----> 不是,工作中不用HashMap
        // 默认等价于什么? -----> Map<String, String> map = new HashMap<>(16,0.75);
        // Map<String, String> map = new HashMap<>();

        // 线程安全
        Map<String,String> map = new ConcurrentHashMap<>();
        for (int i = 1; i <= 30; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
                System.out.println(map);
            },String.valueOf(i)).start();
        }

    }
}

Callable

特点

1、可以有返回值

2、可以抛出异常

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

package callable;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // new Thread(new Runnable()).start();
        // 等价于 new Thread(new FutureTask<V>()).start();
        // new Thread(new FutureTask<V>(Callable)).start();--->FutureTask<V>的构造器为Callable
        new Thread().start();// 怎么启动callable
        MyThread thread = new MyThread();
        FutureTask futureTask = new FutureTask(thread);// 适配类
        new Thread(futureTask,"A").start();
        new Thread(futureTask,"B").start();// 结果会被缓存,提高效率
        // 这个get方法可能会产生阻塞,加入call()是一个耗时操作,则get需要等待返回值
        // 一般将get放在最后,或者使用异步通信来处理
        Integer o = (Integer) futureTask.get();// 获取Callable的返回结果
        System.out.println(o);

    }
}

class MyThread implements Callable<Integer> {


    @Override
    public Integer call() throws Exception {
        System.out.println("call()");
        return 1024;
    }
}

细节:

1、 有缓存

2、结果可能需要等待,会阻塞文章来源地址https://www.toymoban.com/news/detail-724644.html

到了这里,关于JUC并发编程——集合类不安全及Callable(基于狂神说的学习笔记)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Java集合类不安全示例】

    提示:以下是本篇文章正文内容,下面案例可供参考 代码如下(示例): 输出结果 Exception in thread “Thread-1” Exception in thread “Thread-0” Exception in thread “Thread-4” Exception in thread “Thread-25” Exception in thread “Thread-23” Exception in thread “Thread-17” Exception in thread “Thread-18”

    2024年02月03日
    浏览(34)
  • 【并发编程】JUC并发编程(彻底搞懂JUC)

    如果你对多线程没什么了解,那么从入门模块开始。 如果你已经入门了多线程(知道基础的线程创建、死锁、synchronized、lock等),那么从juc模块开始。 新手学技术、老手学体系,高手学格局。 JUC实际上就是我们对于jdk中java.util .concurrent 工具包的简称 ,其结构如下: 这个包

    2024年02月20日
    浏览(51)
  • Java并发编程:Callable、Future和FutureTask

    在前面的文章中我们讲述了创建线程的 2 种方式,一种是直接继承 Thread ,另外一种就是实现 Runnable 接口。 这 2 种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果。 如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起

    2024年02月13日
    浏览(38)
  • 第九章 JUC并发编程

    http://t.csdn.cn/UgzQi 使用 AQS加 Lock 接口实现简单的不可重入锁 早期程序员会自己通过一种同步器去实现另一种相近的同步器,例如用可重入锁去实现信号量,或反之。这显然不够优雅,于是在 JSR166(java 规范提案)中创建了 AQS,提供了这种通用的同步器机制。 AQS 要实现的功能

    2023年04月08日
    浏览(40)
  • 并发编程-JUC-原子类

    JUC 整体概览 原子类 基本类型-使用原子的方式更新基本类型 AtomicInteger:整形原子类 AtomicLong:长整型原子类 AtomicBoolean :布尔型原子类 引用类型 AtomicReference:引用类型原子类 AtomicStampedReference:原子更新引用类型里的字段原子类 AtomicMarkableReference :原子更新带有标记位的引

    2024年02月21日
    浏览(40)
  • JUC并发编程(二)

    JUC并发编程(续) 接上一篇笔记:https://blog.csdn.net/weixin_44780078/article/details/130694996 五、Java内存模型 JMM 即 Java Memory Model,它定义了主存、工作内存抽象概念,底层对应着CPU寄存器、缓存、硬件内存、CPU 指令优化等。 JMM 体现在以下几个方面: 原子性:保证指令不会受到线程

    2024年02月05日
    浏览(93)
  • 【JUC并发编程】

    本笔记内容为狂神说JUC并发编程部分 目录 一、什么是JUC 二、线程和进程 1、概述  2、并发、并行 3、线程有几个状态  4、wait/sleep 区别 三、Lock锁(重点)  四、生产者和消费者问题 五、八锁现象 六、集合类不安全  七、Callable ( 简单 ) 八、常用的辅助类(必会) 1、CountDown

    2024年02月09日
    浏览(40)
  • JUC并发编程之原子类

    目录 1. 什么是原子操作 1.1 原子类的作用 1.2 原子类的常见操作 原子类的使用注意事项 并发编程是现代计算机应用中不可或缺的一部分,而在并发编程中,处理共享资源的并发访问是一个重要的问题。为了避免多线程访问共享资源时出现竞态条件(Race Condition)等问题,J

    2024年02月13日
    浏览(49)
  • JUC 高并发编程基础篇

    • 1、什么是 JUC • 2、Lock 接口 • 3、线程间通信 • 4、集合的线程安全 • 5、多线程锁 • 6、Callable 接口 • 7、JUC 三大辅助类: CountDownLatch CyclicBarrier Semaphore • 8、读写锁: ReentrantReadWriteLock • 9、阻塞队列 • 10、ThreadPool 线程池 • 11、Fork/Join 框架 • 12、CompletableFuture 1 什么

    2024年02月07日
    浏览(52)
  • JUC并发编程14 | ThreadLocal

    尚硅谷JUC并发编程(100-111) ThreadLocal是什么? ThreadLocal 提供 线程局部变量 。这些变量与正常的变量有所不同,因为每一个线程在访问ThreadLocal实例的时候(通过其get或set方法)都有自己的、独立初始化的变量副本。ThreadLocal实例通常是类中的私有静态字段,使用它的目的是

    2024年02月04日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包