我试图使用大小为 4 的线程池(在八核处理器上)在数组列表中添加 1000 万条记录。但与单线程代码相比,它花费的时间是单线程代码的两倍。
下面是代码片段。我可能做错了什么。谁能解释一下代码中的问题是什么?
package com.shree.test;
public class Student {
private int id;
private String name;
private int age;
private int std;
public Student(int id, String name, int age, int std) {
super();
this.id = id;
this.name = name;
this.age = age;
this.std = std;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getStd() {
return std;
}
public void setStd(int std) {
this.std = std;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", age=" + age + ", std=" + std + "]";
}
}
多线程代码(使用线程池):
package com.shree.test;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.RandomUtils;
class Task implements Callable<Student>{
private static final String CHAR_SET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private int id;
private List<Student> studentList;
public Task(int id,List<Student> studentList) {
this.id = id;
this.studentList = studentList;
}
@Override
public Student call() throws Exception {
Student student = new Student(id, RandomStringUtils.random(RandomUtils.nextInt(5, 10), CHAR_SET), RandomUtils.nextInt(10, 15), RandomUtils.nextInt(4, 9));
studentList.add(student);
return student;
}
}
public class MultiThreadStudentListGenerator {
private List<Student> students = Collections.synchronizedList(new ArrayList<>());
private ExecutorService threadPool = Executors.newFixedThreadPool(4);
public void generateStudentList() {
for(int i=0;i<10000000;i++) {
threadPool.submit(new Task(i, students));
}
threadPool.shutdown();
}
public void process() {
generateStudentList();
}
public int getSize() {
return students.size();
}
public void addShutDownhook(LocalDateTime dateTime1 ) {
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
LocalDateTime dateTime2 = LocalDateTime.now();
long diffInMilli = java.time.Duration.between(dateTime1, dateTime2)
.toMillis();
System.out.println("Time taken in Miliseconds: " + diffInMilli);
System.out.println("List Size: " + getSize());
}
});
}
public static void main(String[] args) {
MultiThreadStudentListGenerator multiThreadStudentListGenerator = new MultiThreadStudentListGenerator();
LocalDateTime dateTime1 = LocalDateTime.now();
multiThreadStudentListGenerator.addShutDownhook(dateTime1);
multiThreadStudentListGenerator.process();
}
}
单线程代码:
package com.shree.test;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.RandomUtils;
public class SingleThreadStudentListGenerator {
private static final String CHAR_SET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private List<Student> students = new ArrayList<>();
public void generateStudentList() {
for (int i = 0; i < 10000000; i++) {
Student student = new Student(i, RandomStringUtils.random(RandomUtils.nextInt(5, 10), CHAR_SET),
RandomUtils.nextInt(10, 15), RandomUtils.nextInt(4, 9));
students.add(student);
}
}
public void process() {
generateStudentList();
}
public int getSize() {
return students.size();
}
public void addShutDownhook(LocalDateTime dateTime1) {
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
LocalDateTime dateTime2 = LocalDateTime.now();
long diffInMilli = java.time.Duration.between(dateTime1, dateTime2).toMillis();
System.out.println("Time taken in Miliseconds: " + diffInMilli);
System.out.println("Size: " + getSize());
}
});
}
public static void main(String[] args) {
SingleThreadStudentListGenerator mainClass = new SingleThreadStudentListGenerator();
LocalDateTime dateTime1 = LocalDateTime.now();
mainClass.addShutDownhook(dateTime1);
mainClass.process();
}
}
最佳答案
两个主要问题:
- 如何衡量。您使用关闭 Hook 的想法确实很奇怪。您应该使用像 JMH 这样的框架来进行此类测试,请参阅 here获取如何获得有意义数字的指导
- 那么,这里:
Collections.synchronizedList(new ArrayList<>())
。这将创建一个同步添加/删除请求的列表。这意味着:锁定。你猜怎么着:锁定的成本很高。
换句话说:A)您获取号码的方式是可疑的,B)锁定比不锁定更昂贵。在您的情况下,您的 4 个线程将不断相互冲突,并且必须等待另一个线程完成对列表的修改。
试想一下:当您有一把铲子,并且需要挖一个洞时……雇用 4 个人来做这项工作真的会对您有利吗?或者更愿意是:三个人看着第四个使用铲子?!
如:请注意,使用多个线程只能在特定情况下节省总体执行时间,例如当您的工作负载需要经常等待 I/O 时。 CPU 密集型工作负载(这就是您的代码正在做的事情)不会从“更多线程”中获益。与此相反的。更多线程,这可能意味着上下文切换、锁定、CPU 缓存使用效率较低等。
因此:如果您确实希望看到使用多线程带来的改进,请从肯定会从多线程中受益的工作负载开始。例如:当您连接到 X 网站并下载内容时...那么您将从使用多线程执行此操作中受益匪浅。
关于java - 为什么在多线程环境中用虚拟记录填充数组列表要花费双倍的时间?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58509945/