前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >带你认识线程安全的集合操作

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

作者头像
用户7634691
发布2020-08-10 16:22:04
8390
发布2020-08-10 16:22:04
举报

1、基本认识

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

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

代码语言:javascript
复制
List<String> list = new ArrayList<>();

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

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

代码语言:javascript
复制
List<String> syncList = Collections.synchronizedList(list);

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

Collections.synchronizedList

2、使用场景

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

代码语言:javascript
复制
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 锁。这里截取部分代码,

代码语言:javascript
复制
  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的迭代器并不是线程安全的。

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

代码语言:javascript
复制
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 也要手动进行同步处理。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 犀牛的技术笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、基本认识
  • 2、使用场景
  • 3、源码剖析
  • 4、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档