c#hashset线程安全吗
2017-03-07 · 知道合伙人软件行家
关注
展开全部
前同事问我个问题如何去证明HashSet是否是线程安全的,最近在系统的学习多线程,所以重新想起了这个问题,也写了个demo来证明HashSet不是线程安全的。
什么是线程安全,就是对于数据的读写要线程隔离,不能导致数据的丢失和不一致,每次修改数据都不应该被覆盖掉。
还是举银行取款的经典例子,账户A起初为0,线程A读出0,然后存100(还没写入数据),线程B读出为0,也存100,这个时候最后的账户我们看到的是余额100。这是不科学的,这就叫线程不安全。所以我们要控制存取款的对象,让我们操作数据的对象加锁,更新完数据,其他的线程才能,达到线程安全。
这次我们来证明HashSet,我们知道实现了Set接口。Set的特点就是存放的数据不会重复。因为它的内部会首先读内部保存的数据,是否存在,如果存在就不存放进去,否则存放进去。也就是说数据的存入操作是分两步,首先读取,然后写入。假设不是线程安全,那很可能出现的一种情形就是当线程A判断该set对象没有某个元素,正准备将该元素插入之前,线程B也判断该对象不存在该元素,也准备插入,最后的结果导致了两个相同的元素被插入了。
我们这样来设计demo:
class TestHashSet implements Runnable{
// 实现Runnable 让该集合能被多个线程访问
Set<Integer> set = new HashSet<Integer>();
// 线程的执行就是插入5000个整数
@Override
public void run() {
for (int i = 0;i < 5000;i ++) {
set.add(i);
}
}
}12345678910111234567891011
我们在主线程来测试:
TestHashSet run2 = new TestHashSet();
// 实例化两个线程
Thread t6 = new Thread(run2);
Thread t7 = new Thread(run2);
// 启动两个线程
t6.start();
t7.start();
// 当前线程等待加入到调用线程后
t6.join();
t7.join();
// 打印出集合的size
System.out.println(run2.set.size());123456789101112131415123456789101112131415
打印结果大部分是预期的5000,但是偶尔会出现大于5000的情况。这就出现了之前提到的情况,证明了HashSet不是线程安全的类。
其实查看源代码发现HashSet内部维护数据的采用的是HashMap,根本原因是HashMap不是线程安全的类。导致了HashSet的非线程安全。
什么是线程安全,就是对于数据的读写要线程隔离,不能导致数据的丢失和不一致,每次修改数据都不应该被覆盖掉。
还是举银行取款的经典例子,账户A起初为0,线程A读出0,然后存100(还没写入数据),线程B读出为0,也存100,这个时候最后的账户我们看到的是余额100。这是不科学的,这就叫线程不安全。所以我们要控制存取款的对象,让我们操作数据的对象加锁,更新完数据,其他的线程才能,达到线程安全。
这次我们来证明HashSet,我们知道实现了Set接口。Set的特点就是存放的数据不会重复。因为它的内部会首先读内部保存的数据,是否存在,如果存在就不存放进去,否则存放进去。也就是说数据的存入操作是分两步,首先读取,然后写入。假设不是线程安全,那很可能出现的一种情形就是当线程A判断该set对象没有某个元素,正准备将该元素插入之前,线程B也判断该对象不存在该元素,也准备插入,最后的结果导致了两个相同的元素被插入了。
我们这样来设计demo:
class TestHashSet implements Runnable{
// 实现Runnable 让该集合能被多个线程访问
Set<Integer> set = new HashSet<Integer>();
// 线程的执行就是插入5000个整数
@Override
public void run() {
for (int i = 0;i < 5000;i ++) {
set.add(i);
}
}
}12345678910111234567891011
我们在主线程来测试:
TestHashSet run2 = new TestHashSet();
// 实例化两个线程
Thread t6 = new Thread(run2);
Thread t7 = new Thread(run2);
// 启动两个线程
t6.start();
t7.start();
// 当前线程等待加入到调用线程后
t6.join();
t7.join();
// 打印出集合的size
System.out.println(run2.set.size());123456789101112131415123456789101112131415
打印结果大部分是预期的5000,但是偶尔会出现大于5000的情况。这就出现了之前提到的情况,证明了HashSet不是线程安全的类。
其实查看源代码发现HashSet内部维护数据的采用的是HashMap,根本原因是HashMap不是线程安全的类。导致了HashSet的非线程安全。
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询