[英]Java: Testing a Service for Concurrency
我有一个服务类,其中包含一种将Student
添加到Section
。 现在,每个Section
都有一set
与之关联的Students
。
另外,还有一个Map<Section, Set<Student>>
定义了两者之间的关系。
该service
使用addStudent
方法看起来像这样:
public class MembershipService {
private final Map<Section, Set<Student>> studentsBySection = new HashMap<>();
public void addStudentToSection(Student student, Section sec) {
Set<Student> students = studentsBySection.get(sec);
if (students == null) {
students = new HashSet<>();
studentsBySection.put(sec, students);
}
students.add(student);
}
// ..... also containing helper method : getStudents(Section s)
我需要在多线程方案中测试该功能,在该方案中,我需要显示如果两个或多个线程尝试从公用map
添加或读取学生时会发生什么。
我清楚地知道,用ConcurrentHashMap
替换Hashmap
可以解决我的目的,但无法演示确切的行为。
我的解决方案
我创建了两个线程: Student1
和Student2
并尝试将相同的Service
实例传递给这两个线程并执行添加。 hashmap
预期行为应为ConcurrentModificationException
,而ConcurrentHashMap
则不应引发该行为。 但是它没有显示预期的行为,即使使用HashMap
也能正常工作。 请指导。
这是代码:
学生1
public class Student1 implements Runnable{
Services services;
public Student1(Services ser) {
this.services = ser;
new Thread(this, "Student 1").start();
}
@Override
public void run() {
final Student ALEX = new Student("alex");
services.getMembershipService().addStudentToSection(ALEX,services.getSection());;
try {
System.out.println("Student 1 sleeping");
Thread.sleep(100);
} catch (Exception e) {
System.out.println(e);
}
}
}
学生2
public class Student2 implements Runnable{
Services services;
public Student2(Services ser) {
this.services = ser;
new Thread(this, "Student 2").start();
}
@Override
public void run() {
final Student JOHN = new Student("john");
services.getMembershipService().addStudentToSection(JOHN,services.getSection());;
try {
System.out.println("Student 2 sleeping");
Thread.sleep(100);
} catch (Exception e) {
System.out.println(e);
}
}
}
测试器
public static void main(String[] args) {
final Services services = ServiceFactory.createServices();
final Section A = new Section("A");
services.createSection(A);
Student1 one = new Student1(services);
Student2 two = new Student2(services);
}
我如何证明我的情况?
注意:这与How ConcurrentHashMap works in java
或多线程中的How ConcurrentHashMap works in java
方式无关,请注意。 只是无法使其符合我的要求。
首先, ConcurrentModificationException
仅由迭代器引发,而不是bu put()/get()
: if the map is structurally modified at any time after * the iterator is created, in any way except through the iterator's own * remove method, the iterator will throw a * {@link ConcurrentModificationException}. 由此类的所有“集合视图方法”返回的迭代器都是 :如果在创建迭代器之后的任何时间对结构进行结构修改,则除了通过迭代器自己的remove方法之外,该迭代器将以其他任何方式进行修改抛出* {@link ConcurrentModificationException}。 因此,面对并发的修改,迭代器会迅速而干净地失败,而不是冒着在不确定的将来冒着任意,不确定的行为的风险。
显示Hashmap
在这种情况下不是线程安全的一个好方法是更改您的Section
类,以从hashCode()
方法返回常量(以使失败更快)。
然后,您仅创建1000个不同的Section
对象,并尝试调用服务以将学生映射到多个线程中的section。 基本上,当您完成将学生映射到部分时,地图上的大小将与部分数量不匹配,而将小于不同部分的数量。
您的HashMap和ConcurrentHashMap在多线程环境中相同的原因是由于输入较少。 我要同时读取200个键值对。
只需将代码中的ConcurrentHashMap替换为HashMap即可,您将获得并发ModificationException。
ConcurrentHashMap实现:
package com.java.ConcurrentHashMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapDemo {
private final ConcurrentHashMap<Section,Set<Student>> studentsBySection = new ConcurrentHashMap<>();
public void addStudentToSection(Student student, Section sec) {
//System.out.println(Thread.currentThread().getName());
Set<Student> students = studentsBySection.get(sec);
if (students == null) {
students = new HashSet<>();
studentsBySection.putIfAbsent(sec, students);
}
students.add(student);
}
public static void main(String[] args) {
ConcurrentHashMapDemo ob = new ConcurrentHashMapDemo();
Thread t1 = new Thread(ob.new WriteThreasOne());
t1.setName("one");
Thread t3 = new Thread(ob.new WriteThreasTwo());
t3.setName("three");
Thread t2= new Thread(ob.new ReadThread());
t2.setName("two");
t1.start();
t2.start();
t3.start();
}
class WriteThreasOne implements Runnable {
@Override
public void run() {
final Section A = new Section("A");
for(int i=0;i<100;i++) {
addStudentToSection(new Student("alex"+i),A);
}
}
}
class WriteThreasTwo implements Runnable {
@Override
public void run() {
final Section A = new Section("A");
for(int i=1;i<100;i++) {
addStudentToSection(new Student("sam"+i),A);
}
}
}
class ReadThread implements Runnable {
@Override
public void run() {
//System.out.println(Thread.currentThread().getName());
Iterator<Section> ite = studentsBySection.keySet().iterator();
while(ite.hasNext()){
Section key = ite.next();
System.out.println(key+" : " + studentsBySection.get(key));
}
}
}
}
节类:
package com.java.ConcurrentHashMap;
public class Section {
public Section(String sectionName) {
this.sectionName = sectionName;
}
private String sectionName;
public String getSectionName() {
return sectionName;
}
public void setSectionName(String sectionName) {
this.sectionName = sectionName;
}
@Override
public String toString() {
return "Section [sectionName=" + sectionName + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((sectionName == null) ? 0 : sectionName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Section other = (Section) obj;
if (sectionName == null) {
if (other.sectionName != null)
return false;
} else if (!sectionName.equals(other.sectionName))
return false;
return true;
}
}
学生班:
package com.java.ConcurrentHashMap;
public class Student {
private String studName;
public Student(String studName) {
this.studName = studName;
}
public String getStudName() {
return studName;
}
public void setStudName(String studName) {
this.studName = studName;
}
@Override
public String toString() {
return "Student [ studName=" + studName + "]";
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.