Java 多线程程序没有使用很多 CPU

Java multi-threading programme not using a lot of CPU

我是编程初学者 Java,这是我的第一个多核程序。问题是我的程序从来没有使用超过 13% 的 CPU。不知道自己做的对不对

如何计算得更快并使用更多 CPU 资源?

我的程序包含三个class:

  1. 实例化Work对象的"mainclass线程数

  2. 扩展 Thread 并包含要执行的工作的 "T1" class

  3. A "Work" class 启动所需的线程数并显示所有线程执行工作所花费的时间

这是我的代码 Main class:

public static void main(String[] args) {

  System.out.println("Number of CPUs available = " + Runtime.getRuntime().availableProcessors()); //Display the number of CPUs available
  int iteration = 100000000; // Define a number of itterations to do by all threads

  /*
  Instantiates each work with a different number of threads (1, 4, 8, 12, and 24)
  */
    Work  t1 = new Work(1);
    Work  t4 = new Work(4);
    Work  t8 = new Work(8);
    Work t12 = new Work(12);
    Work t24 = new Work(24);


   /*
    Launch the work for each thread with the specified number of iterations
    */
    t1.goWork(iteration);
    t4.goWork(iteration);
    t8.goWork(iteration);
    t12.goWork(iteration);
    t24.goWork(iteration);

    }

这里是作品 class 代码:

public class Work {

    static long time;     // A variable that each thread increase by the time it takes to complete its task.
    static int itterationPerThread;      // A variable that stores the number of itterations Per Thread to do.
    static int finish;     // A variable that each thread incrase when it finish its task, used to wait until all thread has complete their task.
    private int numberOfThreads;     // The number of threads to launch.
    /**
     *
     * The constructor, set the number Of threads to run
     * @param numberOfThreads
     */
    public Work(int numberOfThreads)
    {
        this.numberOfThreads = numberOfThreads;   //Set the number of threads
    }

    /**
     *
     * A method that launch a specified number of thread in the constructor of the class, and distributes the a number of iteration of each thread.
     * The method does nothing until each thread completes its task and print the time needed for all threads to complete their tasks.
     * @param itterationPerThread
     */
    public void goWork(int itterationPerThread)
    {
        finish = 0;   //Reset the variable in the case that we call the method more than one time
        time = 0;    //Reset the variable in the case that we call the method more than one time
        this.itterationPerThread = itterationPerThread/numberOfThreads;   // Divide the given number of iterations by the number of threads specified in the constructor

        for (int i=0; i<numberOfThreads; i++)   //Launch the specified number of threads
        {
              new T1().run();
        }

        while (finish != numberOfThreads)    //Do nothing until all thread as completed their task
        {
        }
        System.out.println("Time for " + numberOfThreads + " thread = " + time + " ms");   //Display the total time
    }

}

最后是我的 T1 class:

public class T1 extends Thread{

    @Override
    public void run()
    {
        long before = System.currentTimeMillis();

        for (int i=0; i<Work.itterationPerThread; i++) //Get the thread busy with a number of itterations
        {
            Math.cos(2.1545); //Do something...
        }

         long after = System.currentTimeMillis(); //Compute the elapsed time
         Work.time += after - before; //Increase the static variable in Work.java by the time elapsed for this thread
         Work.finish++; // Increase the static variable in Work.java when the thread has finished its job
    }
}

该程序在我的机器上提供了以下输出(四个物理内核和八个超线程):

可用的 CPU 数量 = 8

1 个线程的时间 = 11150 毫秒

4 个线程的时间 = 4630 毫秒

8 个线程的时间 = 2530 毫秒

12 个线程的时间 = 2530 毫秒

24 个线程的时间 = 2540 毫秒

根据我的 CPU 这个结果似乎是正确的,但我的 CPU 使用率从未超过 13%。

我找到了以下 Stack Overflow post,但我并没有真正找到问题的答案。

与其调用 Thread.run() 来实现您的线程所做的事情,不如调用 Thread.start(),这将创建一个新线程并在该新线程上调用 run()

现在您 运行正在 run() 主线程上,而无需创建新线程。由于您有 13% CPU 负载,我预计您有 8 个内核(意味着您已完全填满一个内核)。

更好的方法是创建接口 Runnable 的自定义实现,而不是扩展 Thread。然后你可以 运行 它在线程上如下:

Thread t = new Thread(new MyRunnableTask());
t.start();

这是常用方法,因为它使您可以灵活地(稍后)使用更高级的机制,例如 ExecutorService

编辑: 正如一些评论中所指出的那样。您还从多个线程更改了相同的变量(Work 中的静态变量)。你不应该这样做,因为它允许竞争条件。例如,如 here 所述,增加一个变量可能会导致一个变量。

谢谢大家回答我的问题:

是的,JVM不计算Math.cos(2.1545);在每次迭代中,正如我所说的那样,我已经尝试过 Math.cos(i);和原来的程序有很大的区别!

如前所述,对于多线程,我创建了接口 Runnable 的自定义实现,而不是扩展 Thread,现在使用 Start();方法而不是 运行();

我现在使用 join 方法等待线程完成并删除静态变量。 现在程序使用完整的 CPU 负载和正确的线程数。

仅供参考,这是我的工作新代码 class:

public class Work {

    private Thread[] threadArray; //An array to store a specified number of new threads in the constructor
    /**
     *
     * The constructor, set to the number Of threads to run
     * @param numberOfThreads
     */
    public Work(int numberOfThreads)
    {

        threadArray = new Thread[numberOfThreads];
    }

    /**
     *
     * A methode that launch a specified number of threads in the constructor of the class, and distributes the a number of iteration of each thread.
     * the methode wait until each thread complete their task and print the time needed for all thread to complette their task.
     * @param itterationForAllThread   --> the total of itteration to do by all thread
     */
    public void goWork(int itterationForAllThread)
    {
        long time = 0; // A variable used to compute the elapsed time
        int itterationPerThread; // A variable that store the number of itterations Per Thread to do
        itterationPerThread = itterationForAllThread/threadArray.length; //Divide the given number of itteration by the number of tread specified in the constructor

        for(int i=0; i<threadArray.length; i++) //Launch the specified number of threads
        {
              threadArray[i] = new Thread(new T1(itterationPerThread));  //Create a new thread
              threadArray[i].start();  //Start the job
        }

        long before = System.currentTimeMillis();

        for (Thread thread : threadArray) //For each thread wait until it finish
        {
            try {
                 thread.join(); //Wait for the thread as finish
            }
            catch (InterruptedException ex)
            {
                ex.printStackTrace();
            }
        }
        long after = System.currentTimeMillis();
        time = after - before; //Compute the time elapsed

        System.out.println("Time for " + threadArray.length + " Thread = " + time + " ms"); //Display the total time for the number of threads
    }
}

这里是 T1 class:

public class T1 implements Runnable{

    private int iterrattionPerThread;

    T1(int iterrattionPerThread)
    {
        this.iterrattionPerThread=iterrattionPerThread;
    }


    @Override
    public void run()
    {
        for(int i=0; i<iterrattionPerThread; i++) //Get the thread busy with a number of iterations
        {
            Math.cos(i); //Do something that the JVM can not cache and need to be recaculated every iteration
        }
    }
}