专栏首页犀牛饲养员的技术笔记带你认识线程安全的集合操作

带你认识线程安全的集合操作

1、基本认识

简单来讲,我们平时经常使用的ArrayList不是线程安全的。我们通过使用 Collections.synchronizedList 来包装一个线程安全的ArrayList。

我们一般创建一个普通的list是这样的姿势,

List<String> list = new ArrayList<>();

        // populate the list
        list.add("apple");
        list.add("orange");
        list.add("banana");

然后我们把这个list变成线程安全的list,操作的姿势是这样的,

List<String> syncList = Collections.synchronizedList(list);

这两个list在单线程下使用没有任何不同。

Collections.synchronizedList

2、使用场景

我们来看一个多线程访问list的示例,

public class SynchronizedListTest {

    List<String> list = new ArrayList<>();
    List<String> syncList = Collections.synchronizedList(list);

    @Test
    public void test1() {
        list.add("apple");
        list.add("orange");
        list.add("banana");
        System.out.println("sync list:" + syncList);
        Thread t1 = new FruitManager();
        Thread t2 = new FruitManager();
        t1.start();
        t2.start();
    }

    class FruitManager extends Thread {
        public void run() {
            System.out.println("thread - " + Thread.currentThread().getName() + " start");
            if (syncList.size() > 0) {
                String fruit = (String) syncList.remove(0);
                System.out.println("fruit:" + fruit);
            }

        }
    }

很明显,这种场景如果不使用线程安全的集合,就会造成集合中的元素被重复remove的情况,从而导致程序出错。

3、源码剖析

好的程序员不光要知其然,还要知其所以然。如果只是停留在会用阶段,你永远都只是一个初级的程序员。

Collections.synchronizedList 源码也比较简单,就是给所有的操作就加上了 synchroinzed 锁。这里截取部分代码,

  static class SynchronizedList<E>
        extends SynchronizedCollection<E>
        implements List<E> {
        private static final long serialVersionUID = -7754090372962971524L;

        final List<E> list;

        SynchronizedList(List<E> list) {
            super(list);
            this.list = list;
        }
        SynchronizedList(List<E> list, Object mutex) {
            super(list, mutex);
            this.list = list;
        }

        public boolean equals(Object o) {
            if (this == o)
                return true;
            synchronized (mutex) {return list.equals(o);}
        }
        public int hashCode() {
            synchronized (mutex) {return list.hashCode();}
        }

        public E get(int index) {
            synchronized (mutex) {return list.get(index);}
        }
        public E set(int index, E element) {
            synchronized (mutex) {return list.set(index, element);}
        }
        public void add(int index, E element) {
            synchronized (mutex) {list.add(index, element);}
        }
        public E remove(int index) {
            synchronized (mutex) {return list.remove(index);}
        }

        public ListIterator<E> listIterator() {
            return list.listIterator(); // Must be manually synched by user
        }

        public ListIterator<E> listIterator(int index) {
            return list.listIterator(index); // Must be manually synched by user
        }
    }

这里的 mutex 则是父类 SynchronizedCollection 中的this对象。

细心的读者可能已经看到了,好像迭代器没有加同步锁!是的,也就是说虽然list是安全的,但是list的迭代器并不是线程安全的。

所以官方建议使用的迭代器的时候,要用如下的姿势,

List list = Collections.synchronizedList(new ArrayList());
      ...
  synchronized (list) {
      Iterator i = list.iterator(); // Must be in synchronized block
      while (i.hasNext())
          foo(i.next());
  }

4、总结

  1. SynchronizedList可以将List转成线程安全的集合,在多线程环境下建议使用。
  2. 使用迭代器遍历时,即使用了 SynchronizedList 也要手动进行同步处理。

本文分享自微信公众号 - 犀牛饲养员的技术笔记(coder_start_up),作者:siwuxie18

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-09-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 说说我创业踩过的几个坑

    不过有感于现在的环境,今天想给大家分享几个自己曾经创业踩过的坑。希望能对有创业想法或者已经在创业的道路上折腾的小伙伴有所帮助。

    用户7634691
  • fastjson远程代码执行漏洞问题分析

    fastjson远程代码执行安全漏洞(以下简称RCE漏洞),最早是官方在2017年3月份发出的声明,

    用户7634691
  • 讲得最明白的Elasticsearch源码调试环境搭建教程

    使用elasticsearch(以下简称ES)也有挺长时间了,一直想找机会深入源码研究下。我看源码有个习惯,就是一定要运行起来。不是只把源码下载下来看看就行的。

    用户7634691
  • 面经手册 · 第10篇《扫盲java.util.Collections工具包,学习排序、二分、洗牌、旋转算法》

    好的算法搭配上合适的数据结构,可以让代码功能大大的提升效率。当然,算法学习不只是刷题,还需要落地与应用,否则到了写代码的时候,还是会for循环+ifelse。

    小傅哥
  • Laravel 实现关系模型取出需要的字段

    需求是从建立关系模型的数据表里面取出需要的字段,乱七八糟的不要。一个机构对应多个授权码,授权码里面的信息很杂乱,但是我取出关联模型的时候想把他们过滤掉。

    砸漏
  • python查看所有串口(自学笔记)

    py3study
  • java判断list为空

    list.isEmpty()和list.size()==0 没有区别 isEmpty()判断有没有元素 而size()返回有几个元素 如果判断一个集合有无元素 ...

    似水的流年
  • Todo List: Vue待办事项任务管理 – 第一章

    如果您所在公司有用过类似的todo list产品,那应该对其不是很陌生。todo list(待办事项列表),非常有名的todo list产品有Teambitio...

    Javanx
  • java判断list为空

    if(null == list || list.size() ==0 ){ } list.isEmpty()和list.size()==0 没有区别...

    似水的流年
  • java判断list为空

    if(null == list || list.size() ==0 ){ }

    似水的流年

扫码关注云+社区

领取腾讯云代金券