4
回答
关于HashSet的一点疑惑,请大牛不吝指点,谢谢大家~~
利用AWS快速构建适用于生产的无服务器应用程序,免费试用12个月>>>   

   是这样,我在一个单例模式的测试中,把get到的对象放在HashSet中,利用Set集合的去重,来观察究竟生成的是否为一个实例,但结果却让我很不解,求大家指点,先把代码贴出来。

Singleton.java:

package test;

public class Singleton {

	private static Singleton instance = null;
	// 记录执行了几次new Singleton
	private static int count = 0;

	private Singleton() {
		count++;
	}

	public static int getCount() {
		return count;
	}

	public static Singleton getInstance() {
		if (instance == null) {
			synchronized (Singleton.class) {
				if (instance == null) {
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
}



Test.java:

package test;

import java.util.HashSet;
import java.util.Set;

public class Test {
	
	//创建HashSet结合
	private static Set<Singleton> set = new HashSet<Singleton>();

	public static void main(String[] args) {
		
		//创建实现Runnable接口的对象
		Runnable runnable = new Runnable() {
			@Override
			public void run() {
				Singleton instance = Singleton.getInstance();
				set.add(instance);
			}
		};

		//循环启动线程10000次
		for (int i = 0; i < 10000; i++) {
			new Thread(runnable).start();
		}

		//输出结果
		System.out.println("set.size() == " + set.size());
		System.out.println("set contains : " + set);
		System.out.println("Singleton.count == " + Singleton.getCount());

	}
}



输出结果很奇怪,

每次执行main方法后,输出的set.size()值都是不固定的,基本在1-5之内随机。

但直接控制台输出 set ,里边看到只有一个对象。

Singleton.getCount()方法的输出也永远是1。

不明白为什么第一个输出set.size是什么情况。

结果贴图出来:


举报
风追云影
发帖于1年前 4回/302阅
共有4个答案 最后回答: 1年前
首先明白HashSet的内部是一个HashMap实现,然后因为Singleton.getInstance()是单例,所以它每次的Hashcode都是一样的,也就是说会hash到一个位置上,所以相当于Object obj=Singleton.getInstance();执行了3次 但最终obj值都是一样的。 所以set集合的结果是一个,然后为什么size是随机的呢,因为HashMap/Set是线程不安全的 所以size最后可能是随机的,最后一个更简单,单例嘛,所以。  题外话 建议单例的时候加上volatile关键字
--- 共有 1 条评论 ---
风追云影谢谢,的确是与HashSet的非线程安全有关,通过Collections.synchronizedSet(set)创建的set对象就没有这种情况了。 1年前 回复

一:HashSet底层是HashMap实现的,HashMap不是线程安全。当HashSet在添加元素时会根据元素的hash值来计算数组下标然后再进行操作(添加或者更新),初始时Set没有元素,两个线程使用Set先后添加同一个元素(Singleton),此时通过判断是需要添加而不是更新。线程1对数组元素table[i]进行赋值,size++,此时size为1。线程2也进行添加,由于计算的hash值一致,导致数组下标相同,所以线程2也对同一个数组table[i]进行了赋值,size++,此时size为2。你说的1~5可能出现5个线程都进入到了添加元素代码块,另外的线程则进入了更新元素代码块(更新元素代码块在添加元素代码块之前执行)

二:你这个Singleton.getCount()跟size完全没关系,肯定是1

--- 共有 1 条评论 ---
风追云影谢谢详细解答,通过自己测试和大家的回答我想明白了,感谢~! 1年前 回复

为什么这里的set.size()会大于1?但直接输出set却只能看到一个对象。

另外Singleton.getCount()是另外一个测试创建实例次数的方法,结果和set.size()是互相矛盾的,十分不解。。。

顶部