Java: тестирование сервиса на параллелизм - PullRequest
1 голос
/ 13 мая 2019

У меня есть класс обслуживания, содержащий метод для добавления 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.

Я четко знаю, что замена Hashmap на ConcurrentHashMap решит мою задачу, но не в состоянии продемонстрировать точное поведение.

Мое решение

Я создал два потока: Student1 и Student2 и попытался передать один и тот же экземпляр Service обоим и выполнить добавление.Ожидаемое поведение с hashmap должно быть ConcurrentModificationException, а с ConcurrentHashMap оно не должно выбрасываться.Но он не показывает ожидаемое поведение и работает нормально даже с HashMap.Пожалуйста, руководство.

Вот код:

Student1

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);
    }

}

}

Student2

  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);
    }

}

}

Tester.java

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 или многопоточность в целом. Об этом известно.Я просто не могу привести его в соответствие с моими требованиями.

Ответы [ 2 ]

0 голосов
/ 13 мая 2019

Причина, по которой ваши HashMap и ConcurrentHashMap работают одинаково в многопоточной среде, заключается в меньшем количестве входных данных.Я помещаю и читаю пару ключей-значений 200 одновременно.

Просто замените ConcurrentHashMap на HashMap, в коде вы получите concurrentModificationException.

Реализация 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 + "]";
    }

}
0 голосов
/ 13 мая 2019

Во-первых, ConcurrentModificationException генерируется только итераторами, а не бу put()/get()

Итераторы, возвращаемые представлением коллекции этого класса методы "* are fail-fast : если карта структурно изменена в любое время после * создания итератора, любым способом, кроме через собственный * метод удаления итератора, итератор выдаст исключение * {@link ConcurrentModificationException}. Таким образом, в лицо параллельной * модификации, итератор быстро выходит из строя и чисто, а не рисковать * произвольным недетерминированным поведением в неопределенное время в * будущем.

Хороший способ показать, что Hashmap в этом случае не является потокобезопасным, - это изменить класс Section, чтобы он возвращал константу из метода hashCode() (для ускорения сбоя).

Затем вы просто создаете, скажем, 1000 различных Section объектов и пытаетесь вызвать свой сервис, чтобы отобразить учащихся на секции в нескольких потоках. В основном, когда вы закончите отображать студентов на разделы, размер на карте не будет соответствовать количеству разделов, он будет меньше количества различных разделов.

...