从零学Java 线程安全的集合

这篇具有很好参考价值的文章主要介绍了从零学Java 线程安全的集合。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

线程安全的集合

Collection体系集合、以及线程安全集合。

从零学Java 线程安全的集合,从零学Java,java,安全,开发语言

注:下划线代表线程安全集合

1 List 和 Set体系

Collections中的工具方法

Collections工具类中提供了多个可以获得线程安全集合的方法。

  • public static Collection synchronizedCollection(Collection c)
  • public static List synchronizedList(List list)
  • public static Set synchronizedSet(Set s)
  • public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
  • public static SortedSet synchronizedSortedSet(SortedSet s)
  • public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m)

JDK1.2提供,接口统一、维护性高,但性能没有提升,均以synchonized实现。

1.1 CopyOnWriteArrayList

  • 线程安全的ArrayList,加强版读写分离。
  • 写有锁,读无锁,读写之间不阻塞,优于读写锁。
  • 写入时,先copy一个容器副本、再添加新元素,最后替换引用。
  • 使用方式与ArrayList无异。

1.2 CopyOnWriteArraySet

  • 线程安全的Set,底层使用CopyOnWriteArrayList实现。
  • 唯一不同在于,使用addIfAbsent()添加元素,会遍历数组
  • 如存在元素,则不添加(扔掉副本)。

1.3 ConcurrentHashMap

JDK 1.7

  • 初始容量默认为16段(Segment),使用分段锁设计。
  • 不对整个Map加锁,而是为每个Segment加锁。
  • 当多个对象存入同一个Segment时,才需要互斥。
  • 最理想状态为16个对象分别存入16个Segment,并行数量16。
  • 使用方式与HashMap无异。

JDK 1.8

  • 改为CAS无锁算法。

eg:文章来源地址https://www.toymoban.com/news/detail-795711.html

public class TestThreadSafe {
    public static void main(String[] args) {
		//ArrayList<String> list=new ArrayList<>();
        //CopyOnWriteArrayList list=new CopyOnWriteArrayList();
        //CopyOnWriteArraySet set=new CopyOnWriteArraySet();
        ConcurrentHashMap<String,String> hashMap=new ConcurrentHashMap<>();
        ExecutorService es = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            es.submit(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < 10; j++) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        hashMap.put(Thread.currentThread().getName()+"..."+j,"xxx");
                    }
                }
            });
        }
        es.shutdown();
        while(!es.isTerminated());
        System.out.println("元素个数:"+hashMap.size());
    }
}

2 CAS算法

CAS:Compare And Swap(比较交换算法)

  • 其实现方式是基于硬件平台的汇编指令,是靠硬件来实现的,效率高。
  • 并且比较和交换过程是同步的。
  • CAS是一种乐观锁。

乐观锁:

  • 总是认为是线程安全,不怕别的线程修改变量,如果修改了再重新尝试,直到成功。
  • CAS是乐观锁。

悲观锁:

  • 总是认为线程不安全,不管什么情况都进行加锁,要是获取锁失败,就阻塞。
  • synchronized、ReentrantLock是悲观锁。

CAS比较交换算法,修改的方法包含三个核心参数(V,E,N)

  • V:要更新的变量、E:预期值、N:新值。
  • 只有当V==E时,V=N;否则表示已被更新过,则取消当前操作,继续判断直到成功。

eg:

使用代码模拟CAS算法

public class TestCAS {
    public static void main(String[] args) {
        Cas cas=new Cas();
        ExecutorService es = Executors.newFixedThreadPool(100);
        for (int i = 0; i < 100; i++) {
            es.submit(() -> {
                while(true) {
                    int old = cas.getV();
                    boolean b = cas.compareAndSwap(old, new Random().nextInt(100));
                    System.out.println(Thread.currentThread().getName()+"..."+b);
                    if(b){
                        break;
                    }
                }
            });
        }
        es.shutdown();
    }
    static class Cas{
        private int V;//更新的变量
        //获取V的值
        public int getV(){
            return V;
        }
        public synchronized boolean compareAndSwap(int E,int N){
            if(E==V){
                V=N;
                return true;
            }
            return false;
        }
    }
}

3 Queue接口(队列)

Collection的子接口,表示队列FIFO(First In First Out)

常用方法:

  • 抛出异常:
    • boolean add(E e) //顺序添加一个元素(到达上限后,再添加则会抛出异常)
    • E remove() //获得第一个元素并移除(如果队列没有元素时,则抛异常)
    • E element() //获得第一个元素但不移除(如果队列没有元素时,则抛异常)
  • 返回特殊值:推荐使用
    • boolean offer(E e) //顺序添加一个元素 (到达上限后,再添加则会返回false)
    • E poll() //获得第一个元素并移除 (如果队列没有元素时,则返回null)
    • E peek() //获得第一个元素但不移除 (如果队列没有元素时,则返回null)

3.1 ConcurrentLinkedQueue

线程安全、可高效读写的队列,高并发下性能最好的队列。

  • 采用CAS比较交换算法

eg:

public class TestQueue {
    public static void main(String[] args) {
//        Queue<String> queue=new LinkedList<>();
        Queue<String> queue=new ConcurrentLinkedQueue<>();
        //入队
//        queue.offer("aaa");
//        queue.offer("bbb");
//        queue.offer("ccc");
        ExecutorService es = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 3; i++) {
            es.submit(new Runnable() {
                @Override
                public void run() {
                    queue.offer(Thread.currentThread().getName());
                }
            });
        }
        //出队
        es.shutdown();
        while(!es.isTerminated());
        int count=queue.size();
        for (int i = 0; i < count; i++) {
            System.out.println(queue.poll());
        }
    }
}

3.2 BlockingQueue接口(阻塞队列)

Queue的子接口,阻塞的队列,增加了两个线程状态为无限期等待的方法。

方法:

  • void put(E e) //将指定元素插入此队列中,如果没有可用空间,则等待。
  • E take() //获取并移除此队列头部元素,如果没有可用元素,则等待。
  • 可用于解决生产者、消费者问题。

实现类:

  • ArrayBlockingQueue:数组结构实现,有界队列。(手工固定上限)
  • LinkedBlockingQueue:链表结构实现,有界队列。(默认上限Integer.MAX_VALUE)

eg:

package StageOne.day21.demo02;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * @author 胡昊龙
 * @version 1.0
 * @description: TODO
 * @date 2024/1/16 14:32
 */
public class TestBlockingQueue {
    public static void main(String[] args) {
        //创建阻塞队列
        BlockingQueue<String> queue = new LinkedBlockingQueue<>(6);
        //创建线程池
        ExecutorService es = Executors.newCachedThreadPool();
        //提交任务
        es.submit(()->{
            for (int i = 0; i < 30; i++) {
                try {
                    queue.put("面包"+i);
                    System.out.println("生产了"+i);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        es.submit(()->{
            for (int i = 0; i < 30; i++) {
                try {
                    String take = queue.take();
                    System.out.println("消费了"+take);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        es.shutdown();
    }
}

到了这里,关于从零学Java 线程安全的集合的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • Java 多线程之线程安全集合

    集合关系图 本文主要关注线程安全的集合,如 List、Set、Queue、Map 等接口的线程安全的实现方式,有关集合基础知识请转到这里。所谓线程安全集合,就是在多线程环境中使用集合不会导致数据不一致和数据异常的集合。在 Java 中线程安全集现在基本都使用 java.util.concurrent

    2024年02月05日
    浏览(49)
  • 从零学Java - String类

    Java程序中的所有字符串文本(例如“abc”)都是此类的实例。 字符串字面值是常量,具有不可变性,创建之后不可改变。 1.1 常用两种创建方式 1.2 比较两种创建方式 字符串字面值保存在常量池中,JDK1.7之前常量池存在方法区中,JDK1.7(包括1.7)之后移入堆中;常量池中数据

    2024年02月02日
    浏览(37)
  • 从零学Java 单例模式

    单例(Singleton):保证只能创建一个该类的对象。 实现单例三个步骤 私有化构造方法 在类内部创建一个对象 在类中添加一个公开的方法,返回单例对象 2.1 饿汉式 饿汉式: 类加载时, 对象则实例化 代码实现: SingleTon: Test: 2.2 懒汉式 懒汉式: 使用时创建, 线程不安全 代码实现

    2024年01月23日
    浏览(32)
  • java基础之线程安全问题以及线程安全集合类

    当多个线程同时访问同一个临界资源时,原子操作可能被破坏,会导致数据丢失, 就会触发线程安全问题 临界资源: 被多个线程同时访问的对象 原子操作: 线程访问临界资源的过程中不可更改和缺失的操作 互斥锁 每个对象都默认拥有互斥锁, 该锁默认不开启. 当开启互斥锁之后

    2024年01月18日
    浏览(60)
  • Java 8并发集合:安全高效的多线程集合

    在多线程环境中,使用线程安全的数据结构非常重要,以避免竞态条件和数据不一致的问题。Java 8引入了一些并发集合类,提供了安全高效的多线程集合操作。本教程将介绍Java 8中的并发集合类,包括ConcurrentHashMap、ConcurrentLinkedQueue、ConcurrentSkipListSet和CopyOnWriteArrayList。 Conc

    2024年02月04日
    浏览(55)
  • 【JAVA】哪些集合类是线程安全的

    🍎 个人博客: 个人主页 🏆 个人专栏: JAVA ⛳️   功不唐捐,玉汝于成 目录 前言 正文 Vector: HashTable: Collections.synchronizedList()、Collections.synchronizedSet()、Collections.synchronizedMap(): 4.ConcurrentHashMap: 5.CopyOnWriteArrayList 和 CopyOnWriteArraySet: 结语  我的其他博客 在多线程编程中

    2024年01月25日
    浏览(38)
  • JAVA数据结构篇--13线程安全的Set 集合

    前言:java 中用于存放不重复元素的set 集合,其中无序的HashSet,以及有序的LinkedHashSet和TreeSet 都是非线程安全的,那么多线程环境下,我们要存放不重复的元素,需要使用哪种集合进行数据存取; 1 使用: 2 过程: 2.1 放入获取元素: Collections.synchronizedSet:通过使用synchron

    2024年02月16日
    浏览(41)
  • Java - JUC(java.util.concurrent)包详解,其下的锁、安全集合类、线程池相关、线程创建相关和线程辅助类、阻塞队列

    JUC是java.util.concurrent包的简称,在Java5.0添加,目的就是为了更好的支持高并发任务。让开发者进行多线程编程时减少竞争条件和死锁的问题 java.lang.Thread.State tools(工具类):又叫信号量三组工具类,包含有 CountDownLatch(闭锁) 是一个同步辅助类,在完成一组正在其他线程中

    2024年02月05日
    浏览(35)
  • 【JavaEE】JUC(java.util.concurrent)的常见类以及线程安全的集合类

    目录 1、JUC(java.util.concurrent)的常见类 1.1、Callable接口的用法(创建线程的一种写法)  1.2、ReentrantLock可重入互斥锁 1.2.1、ReentrantLock和synchronized的区别  1.2.2、如何选择使用哪个锁 1.3、Semaphore信号量 1.4、CountDownLatch  2、线程安全的集合类 2.1、多线程环境使用ArrayList  2.2、

    2024年02月07日
    浏览(49)
  • 码出高效:Java开发手册笔记(线程安全)

        并发与并行的目标都是尽可能快地执行完所有任务。以医生坐诊为例,某个科室有两个专家同时出诊,这就是两个并行任务,其中一个医生,时而问诊,时而查看化验单,然后继续问诊,突然又中断去处理病人的咨询,这就是并发。在并发环境下,由于程序的封闭性全

    2024年02月08日
    浏览(39)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包