gpt4 book ai didi

java - 为什么在多线程环境中用虚拟记录填充数组列表要花费双倍的时间?

转载 作者:行者123 更新时间:2023-12-02 09:23:28 24 4
gpt4 key购买 nike

我试图使用大小为 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/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com