除非将Thread.sleep(1)放入线程的run()方法中,否则有2个以上的线程会比1或2个线程运行慢

时间:2019-04-27 20:50:45

标签: java multithreading threadpool executorservice collatz

我要实现的任务是使用多个线程在设定的间隔中找到数字的Collat​​z序列,并查看与一个线程相比有多少改进。

但是无论我选择2个线程,一个线程总是总是更快(编辑.2个线程更快,但是4个线程比1个线程慢,而且速度不算多,我也不知道为什么。(我什至可以说线程越多,速度越慢)。我希望有人可以解释。也许我做错了。

下面是我到目前为止编写的代码。我正在使用ThreadPoolExecutor执行任务(一个任务=一个Collat​​z序列,一个间隔中的一个数字)。

Collat​​z类:

    public class ParallelCollatz implements Runnable {
    private long result;
    private long inputNum;

    public long getResult() {
        return result;
    }
    public void setResult(long result) {
        this.result = result;
    }
    public long getInputNum() {
        return inputNum;
    }
    public void setInputNum(long inputNum) {
        this.inputNum = inputNum;
    }
    public void run() {

        //System.out.println("number:" + inputNum);
        //System.out.println("Thread:" + Thread.currentThread().getId());
        //int j=0;
        //if(Thread.currentThread().getId()==11) {
        //  ++j;
        //  System.out.println(j);
        //}

            long result = 1;

            //main recursive computation
            while (inputNum > 1) {

                if (inputNum % 2 == 0) {
                    inputNum = inputNum / 2;
                } else {
                    inputNum = inputNum * 3 + 1;
                }
                ++result;
            }
           // try {
                //Thread.sleep(10);
            //} catch (InterruptedException e) {
                // TODO Auto-generated catch block
        //      e.printStackTrace();
            //}
            this.result=result;
            return;
        }

}

运行线程的主类(是的,我现在创建两个具有相同编号的列表,因为使用一个线程运行后,初始值会丢失):

        ThreadPoolExecutor executor = (ThreadPoolExecutor)Executors.newFixedThreadPool(1);
    ThreadPoolExecutor executor2 = (ThreadPoolExecutor)Executors.newFixedThreadPool(4);

    List<ParallelCollatz> tasks = new ArrayList<ParallelCollatz>();
    for(int i=1; i<=1000000; i++) {
        ParallelCollatz task = new ParallelCollatz();
        task.setInputNum((long)(i+1000000));
        tasks.add(task);

    }


    long startTime = System.nanoTime();
    for(int i=0; i<1000000; i++) {
        executor.execute(tasks.get(i));

    }

    executor.shutdown();
    boolean tempFirst=false;
    try {
        tempFirst =executor.awaitTermination(5, TimeUnit.HOURS);
    } catch (InterruptedException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }
    System.out.println("tempFirst " + tempFirst);
     long endTime = System.nanoTime();
    long    durationInNano = endTime - startTime;
    long    durationInMillis = TimeUnit.NANOSECONDS.toMillis(durationInNano);  //Total execution time in nano seconds
        System.out.println("laikas " +durationInMillis);


        List<ParallelCollatz> tasks2 = new ArrayList<ParallelCollatz>();
        for(int i=1; i<=1000000; i++) {
            ParallelCollatz task = new ParallelCollatz();
            task.setInputNum((long)(i+1000000));
            tasks2.add(task);

        }


        long startTime2 = System.nanoTime();
        for(int i=0; i<1000000; i++) {
            executor2.execute(tasks2.get(i));

        }

        executor2.shutdown();
        boolean temp =false;
        try {
             temp=executor2.awaitTermination(5, TimeUnit.HOURS);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("temp "+ temp);
         long endTime2 = System.nanoTime();
            long durationInNano2 = endTime2 - startTime2;
            long durationInMillis2 = TimeUnit.NANOSECONDS.toMillis(durationInNano2);  //Total execution time in nano seconds
            System.out.println("laikas2 " +durationInMillis2);

例如,使用一个线程运行,它可以在3280毫秒内完成。用两个线程运行3437ms。我应该考虑使用其他并发结构来计算每个元素吗?

编辑 澄清。我不是要并行化各个序列,而是要使每个数字具有序列时要间隔一个数字。(与其他数字无关)

EDIT2

今天,我在具有6个核心和12个逻辑处理器的优秀PC上运行了该程序,问题仍然存在。有谁知道问题可能在哪里?我还更新了我的代码。 4个线程由于某些原因比2个线程差(甚至比1个线程差)。我也应用了答案中给出的内容,但没有任何变化。

另一个编辑 我注意到的是,如果将Thread.sleep(1)放入ParallelCollat​​z方法中,则性能会随着线程数的增加而逐渐提高。也许这个细节告诉别人出了什么问题?但是,如果没有Thread.Sleep(1),无论我执行多少任务,第二个线程的执行速度最快,第二个线程的执行速度最快,而其他线程则挂起了类似的毫秒数,但都比1和2个线程慢。

新编辑 我还尝试在Runnable类的run()方法中放置更多任务(用于计算不是1而是10或100个Collat​​z序列的循环),以便线程本身可以完成更多工作。不幸的是,这也没有帮助。 也许我启动任务不正确?任何想法吗?

编辑 因此,在向run方法添加更多任务之后,似乎可以对其进行一些修复,但是对于更多线程,该问题仍然保持8+。我仍然想知道这是因为创建和运行线程比执行任务花费更多的时间吗?还是我应该针对这个问题创建新帖子?

1 个答案:

答案 0 :(得分:2)

您不是在等待任务完成,而是在衡量将其提交给执行者所花费的时间。

2019-04-27 21:04:25.650 21326-21326/com.example.hellojni I/zygote: at android.view.View android.view.LayoutInflater.inflate(org.xmlpull.v1.XmlPullParser, android.view.ViewGroup, boolean) (LayoutInflater.java:515) 2019-04-27 21:04:25.650 21326-21326/com.example.hellojni I/zygote: at android.view.View android.view.LayoutInflater.inflate(int, android.view.ViewGroup, boolean) (LayoutInflater.java:423) 2019-04-27 21:04:25.650 21326-21326/com.example.hellojni I/zygote: at android.view.View android.view.LayoutInflater.inflate(int, android.view.ViewGroup) (LayoutInflater.java:374) 2019-04-27 21:04:25.650 21326-21326/com.example.hellojni I/zygote: at android.view.ViewGroup android.support.v7.app.AppCompatDelegateImpl.createSubDecor() (AppCompatDelegateImpl.java:607) 2019-04-27 21:04:25.650 21326-21326/com.example.hellojni I/zygote: at void android.support.v7.app.AppCompatDelegateImpl.ensureSubDecor() (AppCompatDelegateImpl.java:518) 2019-04-27 21:04:25.650 21326-21326/com.example.hellojni I/zygote: at void android.support.v7.app.AppCompatDelegateImpl.setContentView(int) (AppCompatDelegateImpl.java:466) 2019-04-27 21:04:25.650 21326-21326/com.example.hellojni I/zygote: at void android.support.v7.app.AppCompatActivity.setContentView(int) (AppCompatActivity.java:140) 2019-04-27 21:04:25.650 21326-21326/com.example.hellojni I/zygote: at void com.example.hellojni.HelloJni.onCreate(android.os.Bundle) (HelloJni.java:75) 2019-04-27 21:04:25.650 21326-21326/com.example.hellojni I/zygote: at void android.app.Activity.performCreate(android.os.Bundle, android.os.PersistableBundle) (Activity.java:7009) 2019-04-27 21:04:25.650 21326-21326/com.example.hellojni I/zygote: at void android.app.Activity.performCreate(android.os.Bundle) (Activity.java:7000) 2019-04-27 21:04:25.650 21326-21326/com.example.hellojni I/zygote: at void android.app.Instrumentation.callActivityOnCreate(android.app.Activity, android.os.Bundle) (Instrumentation.java:1214) 2019-04-27 21:04:25.650 21326-21326/com.example.hellojni I/zygote: at android.app.Activity android.app.ActivityThread.performLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent) (ActivityThread.java:2731) 2019-04-27 21:04:25.650 21326-21326/com.example.hellojni I/zygote: at void android.app.ActivityThread.handleLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:2856) 2019-04-27 21:04:25.650 21326-21326/com.example.hellojni I/zygote: at void android.app.ActivityThread.-wrap11(android.app.ActivityThread, android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:-1) 2019-04-27 21:04:25.650 21326-21326/com.example.hellojni I/zygote: at void android.app.ActivityThread$H.handleMessage(android.os.Message) (ActivityThread.java:1589) 2019-04-27 21:04:25.650 21326-21326/com.example.hellojni I/zygote: at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:106) 2019-04-27 21:04:25.650 21326-21326/com.example.hellojni I/zygote: at void android.os.Looper.loop() (Looper.java:164) 2019-04-27 21:04:25.650 21326-21326/com.example.hellojni I/zygote: at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:6494) 2019-04-27 21:04:25.650 21326-21326/com.example.hellojni I/zygote: at java.lang.Object java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object[]) (Method.java:-2) 2019-04-27 21:04:25.650 21326-21326/com.example.hellojni I/zygote: at void com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run() (RuntimeInit.java:438) 2019-04-27 21:04:25.650 21326-21326/com.example.hellojni I/zygote: at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:807) 2019-04-27 21:04:25.651 21326-21326/com.example.hellojni I/zygote: Rejecting re-init on previously-failed class java.lang.Class<android.support.v4.view.ViewCompat$OnUnhandledKeyEventListenerWrapper>: java.lang.NoClassDefFoundError: Failed resolution of: Landroid/view/View$OnUnhandledKeyEventListener; 2019-04-27 21:04:25.651 21326-21326/com.example.hellojni I/zygote: at void android.support.v4.view.ViewCompat.setBackground(android.view.View, android.graphics.drawable.Drawable) (ViewCompat.java:2341) 2019-04-27 21:04:25.651 21326-21326/com.example.hellojni I/zygote: at void android.support.v7.widget.ActionBarContainer.<init>(android.content.Context, android.util.AttributeSet) (ActionBarContainer.java:62) 2019-04-27 21:04:25.651 21326-21326/com.example.hellojni I/zygote: at java.lang.Object java.lang.reflect.Constructor.newInstance0(java.lang.Object[]) (Constructor.java:-2) 2019-04-27 21:04:25.651 21326-21326/com.example.hellojni I/zygote: at java.lang.Object java.lang.reflect.Constructor.newInstance(java.lang.Object[]) (Constructor.java:334) 2019-04-27 21:04:25.651 21326-21326/com.example.hellojni I/zygote: at android.view.View android.view.LayoutInflater.createView(java.lang.String, java.lang.String, android.util.AttributeSet) (LayoutInflater.java:647) 2019-04-27 21:04:25.651 21326-21326/com.example.hellojni I/zygote: at android.view.View android.view.LayoutInflater.createViewFromTag(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet, boolean) (LayoutInflater.java:790) 2019-04-27 21:04:25.651 21326-21326/com.example.hellojni I/zygote: at android.view.View android.view.LayoutInflater.createViewFromTag(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet) (LayoutInflater.java:730) 2019-04-27 21:04:25.651 21326-21326/com.example.hellojni I/zygote: at void android.view.LayoutInflater.rInflate(org.xmlpull.v1.XmlPullParser, android.view.View, android.content.Context, android.util.AttributeSet, boolean) (LayoutInflater.java:863) 2019-04-27 21:04:25.651 21326-21326/com.example.hellojni I/zygote: at void android.view.LayoutInflater.rInflateChildren(org.xmlpull.v1.XmlPullParser, android.view.View, android.util.AttributeSet, boolean) (LayoutInflater.java:824) 不会等待所有任务完成。您需要在此之后致电executor.shutdown()

executor.awaitTermination

https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html#shutdown()

更新 我相信我们的测试方法是有缺陷的。我在我的机器(1个处理器,2个内核,4个逻辑处理器)上重复了测试,并且每次运行所花费的时间差异很大。

我相信以下是主要原因:

  • JVM启动和JIT编译时间。最初,代码以解释模式运行。
  • 计算结果被忽略。我没有直觉,JIT删除了什么,我们实际上正在测量什么。
  • 代码中的打印行

要对此进行测试,我将您的测试转换为JMH。 特别是:

  • 我将可运行对象转换为可调用对象,并返回结果的总和以防止内联(或者,可以使用JMH的BlackHole)
  • 我的任务没有状态,我将所有运动部件移动到了局部变量。无需GC即可清理任务。
  • 我仍然在每一轮中创建执行人。这不是完美的,但我决定保持原样。

我在下面收到的结果符合我的期望:一个内核在主线程中等待,工作在单个内核上执行,数量几乎相同。

executor.shutdown();
executor.awaitTermination(5, TimeUnit.HOURS);

更新的代码:

Benchmark                  Mode  Cnt    Score    Error  Units
SpeedTest.multipleThreads  avgt   20  559.996 ± 20.181  ms/op
SpeedTest.singleThread     avgt   20  562.048 ± 16.418  ms/op

和基准本身:

public class ParallelCollatz implements Callable<Long> {

    private final long inputNumInit;

    public ParallelCollatz(long inputNumInit) {
        this.inputNumInit = inputNumInit;
    }


    @Override
    public Long call() {
        long result = 1;
        long inputNum = inputNumInit;
        //main recursive computation
        while (inputNum > 1) {

            if (inputNum % 2 == 0) {
                inputNum = inputNum / 2;
            } else {
                inputNum = inputNum * 3 + 1;
            }
            ++result;
        }
        return result;
    }

}