前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >线程安全集合类中的对象是安全的么?

线程安全集合类中的对象是安全的么?

作者头像
FunTester
发布2020-04-03 16:07:32
6010
发布2020-04-03 16:07:32
举报
文章被收录于专栏:FunTesterFunTester

之前的文章Java并发BUG基础篇中提到过线程安全的集合类如CopyOnWriteArrayListConcurrentHashMap等的使用,以及线程安全类的几种创建方法:

代码语言:javascript
复制

Map<String, String> map = Collections.synchronizedMap(new HashMap<>());

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

CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

Map<String, String> map = new ConcurrentHashMap<>();

这些JDK中自带的集合类是非常好用的,使用方法非常简单,大家有需求的可以自己写个Demo测试一下。下面是我写的一个Demo,为了验证一个问题:如何在线程安全的类中存放不安全的对象,那么对于集合中对象的访问是线程安全的吗?

下面是我测试在集合中存放不安全的对象的Demo:

代码语言:javascript
复制
package com.fun

import com.fun.base.constaint.ThreadLimitTimesCount
import com.fun.frame.SourceCode

import java.util.concurrent.ConcurrentHashMap

class TSSS extends SourceCode {

    static ConcurrentHashMap<Integer, List<Integer>> map = new ConcurrentHashMap<>()

    static List<Integer> list = new ArrayList<>()


    public static void main(String[] args) {
        map.put(1, list)
        30.times {
            list.add(4)
        }
        def tt = new TT(5)

        new com.fun.frame.excute.Concurrent(tt * 5).start()

        output(map.get(1).size())
    }

    static class TT extends ThreadLimitTimesCount {

        public TT(int time) {
            super(null, time, null)
        }

        @Override
        protected void doing() throws Exception {
            list.remove(3)
        }

        public TT clone() {
            return new TT(times)
        }
    }
}

控制台输出结果如下:

代码语言:javascript
复制
INFO-> 当前用户:fv,IP:192.168.0.100,工作目录:/Users/fv/Documents/workspace/fun/,系统编码格式:UTF-8,系统Mac OS X版本:10.15.3
INFO-> 执行次数:5,错误次数: 0,总耗时:1 s
INFO-> 执行次数:5,错误次数: 0,总耗时:1 s
INFO-> 执行次数:5,错误次数: 0,总耗时:1 s
INFO-> 执行次数:5,错误次数: 0,总耗时:1 s
INFO-> 执行次数:5,错误次数: 0,总耗时:1 s
INFO-> 总计5个线程,共用时:0.109 s,执行总数:25,错误数:0,失败数:0
INFO-> 数据保存成功!文件名:/Users/fv/Documents/workspace/fun/long/5FunTester
INFO->
~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~ JSON ~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~
>  {
>  ① . "rt":3,
>  ① . "total":25,
>  ① . "qps":1329.787234042553,
>  ① . "excuteTotal":25,
>  ① . "failRate":0.0,
>  ① . "threads":5,
>  ① . "startTime":"2020-02-24 18:13:23",
>  ① . "endTime":"2020-02-24 18:13:23",
>  ① . "errorRate":0.0,
>  ① . "table":"",
>  ① . "desc":"FunTester"
>  }
~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~ JSON ~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~
INFO->
INFO-> 8

Process finished with exit code 0

最后输出结果是8,可见:在线程安全集合中存放的非线程安全类依然是不安全的,具体原因可以从list.remove()方法中得见:

代码语言:javascript
复制
    public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

先检查,然后去处理一些数据,然后将index后面的数组复制一遍向前移动一位索引,然后就数组最后一位设置为null并将数组的size减一。在并发状况下,可能会有多个线程进行数组拷贝时使用的是一个sizeindex是固定的,因为之前访问这个list的线程并没有完成对size的修改赋值。

下面我将ArrayList替换成线程安全的vector类,代码如下:

代码语言:javascript
复制
package com.fun

import com.fun.base.constaint.ThreadLimitTimesCount
import com.fun.frame.SourceCode

import java.util.concurrent.ConcurrentHashMap

class TSSS extends SourceCode {

    static ConcurrentHashMap<Integer, List<Integer>> map = new ConcurrentHashMap<>()

    static List<Integer> list = new Vector<>()


    public static void main(String[] args) {
        map.put(1, list)
        30.times {
            list.add(4)
        }
        def tt = new TT(5)

        new com.fun.frame.excute.Concurrent(tt * 5).start()

        output(map.get(1).size())
    }

    static class TT extends ThreadLimitTimesCount {

        public TT(int time) {
            super(null, time, null)
        }

        @Override
        protected void doing() throws Exception {
            map.get(1).remove(3)
        }

        public TT clone() {
            return new TT(times)
        }
    }
}

控制台控制台输出结果如下:

代码语言:javascript
复制
INFO-> 当前用户:fv,IP:192.168.0.100,工作目录:/Users/fv/Documents/workspace/fun/,系统编码格式:UTF-8,系统Mac OS X版本:10.15.3
INFO-> 执行次数:5,错误次数: 0,总耗时:1 s
INFO-> 执行次数:5,错误次数: 0,总耗时:1 s
INFO-> 执行次数:5,错误次数: 0,总耗时:1 s
INFO-> 执行次数:5,错误次数: 0,总耗时:1 s
INFO-> 执行次数:5,错误次数: 0,总耗时:1 s
INFO-> 总计5个线程,共用时:0.115 s,执行总数:25,错误数:0,失败数:0
INFO-> 数据保存成功!文件名:/Users/fv/Documents/workspace/fun/long/5FunTester
INFO->
~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~ JSON ~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~
>  {
>  ① . "rt":7,
>  ① . "total":25,
>  ① . "qps":710.2272727272727,
>  ① . "excuteTotal":25,
>  ① . "failRate":0.0,
>  ① . "threads":5,
>  ① . "startTime":"2020-02-24 18:27:57",
>  ① . "endTime":"2020-02-24 18:27:57",
>  ① . "errorRate":0.0,
>  ① . "table":"",
>  ① . "desc":"FunTester"
>  }
~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~ JSON ~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~
INFO->
INFO-> 5

Process finished with exit code 0

经过多次运行,最后输出依然是5,说明线程是安全的。


  • 郑重声明:文章首发于公众号“FunTester”,禁止第三方(腾讯云除外)转载、发表。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-02-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FunTester 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档