簡體   English   中英

簡單的Java多線程測試不起作用

[英]Simple Java multihreading test not working

我寫了一些java代碼,使用while循環可以在1秒內增加一個值。

然后我嘗試使用4個線程在1秒內盡可能多地增加四個值。 我期待有四個線程,與簡單循環相比,我會得到四倍的值。

代碼如下:

package threadTest;

public class ThreadTestMain {

public static void main(String[] args) {

    long end_time = 0;
    long value = 0;
    long runtime = 0;

    long start_time = System.currentTimeMillis();
    while (runtime<=1000) {
        value++;
        runtime = System.currentTimeMillis() - start_time;
    }

    System.out.println(value);
    end_time = System.currentTimeMillis();
    System.out.println("DEBUG-> Time should be 1sec : " + (end_time - start_time));

    SeperateExecution myObject = new SeperateExecution(0);
    SeperateExecution myObject2 = new SeperateExecution(0);
    SeperateExecution myObject3 = new SeperateExecution(0);
    SeperateExecution myObject4 = new SeperateExecution(0);

    Thread worker1 = new Thread(myObject,"worker1");
    Thread worker2 = new Thread(myObject2,"worker2");
    Thread worker3 = new Thread(myObject3,"worker3");
    Thread worker4 = new Thread(myObject4,"worker4");

    worker1.start();worker2.start();worker3.start();worker4.start();
}

}

package threadTest;

//import java.util.concurrent.atomic.AtomicLong;

public class SeperateExecution implements Runnable {
private long value = 0;
//private AtomicLong value = null;
private long start_time = 0;

public SeperateExecution (long p_val)
{
    this.value = p_val;
    //this.value = new AtomicLong(p_val);
    //this.start_time = p_st;
}

@Override
public void run(){
    long runtime = 0;
    this.start_time = System.currentTimeMillis();
    while (runtime<=1000) {
        //value.incrementAndGet();
        value++;
        runtime = System.currentTimeMillis() - start_time;
    }
    System.out.println(Thread.currentThread().getName() + " DONE " + value + 
            " Thread Exection Time " + (System.currentTimeMillis()- start_time));
}
}

代碼輸出如下:

52266551
DEBUG-> Time should be 1sec : 1001
worker1 DONE 13364059 Thread Exection Time 1001
worker2 DONE 13744972 Thread Exection Time 1001
worker4 DONE 11652084 Thread Exection Time 1001
worker3 DONE 13605645 Thread Exection Time 1001

有人可以幫助我理解為什么多線程值總和一個小於正常while循環的值?

一個類比,以幫助理解我的問題:如果一個工人一天可以挖一個洞,那么四個工人一天可以挖四個洞。

謝謝

你的結果的原因似乎相當明顯: System.currentTimeMillis在某個地方獲得了一個鎖,這使得代碼基本上是順序的(內核調用是迄今為止在循環中完成的最昂貴的事情)。

在這種情況下,您希望四個線程​​的數量與順序版本大致相同,這幾乎就是發生的情況。

有趣的問題是為什么System.currentTimeMillis需要鎖定? 它是一個在src/os/<os>/vm/os_<os>.cpp中實現的本機函數,其中<os>是linux,windows或solaris。 稍微簡化的函數在jdk9開發樹中如下所示:

jlong os::javaTimeMillis() {
    FILETIME wt;
    GetSystemTimeAsFileTime(&wt);
    return windows_to_java_time(wt);
}

因此,Java本身不會同步,因為GetSystemTimeAsFileTime是線程安全的。 因此我們可以假設操作系統內部獲取該方法的鎖定 - 相當令人驚訝,我真的不明白為什么這是必要的...使用的結構只有8個字節,因此可以原子更新。

無論如何就是這樣,如果在Linux上運行相同的代碼不會表現出這樣的行為,我不會感到驚訝。

在我看來,不可能在所有線程中獲得相同的數字。

  1. 你可能有4個處理器,但沒有必要全部四個專用於你的java程序。 它可以將循環空間交換到其他進程(如OS進程,其他進程正在運行..)

  2. 我在沒有System.currentMill的情況下使用簡單循環清理了代碼

這是結果

Thread3 Value :413907923
Thread1 Value :431271274
Thread2 Value :426025618
Thread4 Value :431386076

我嘗試了8個線程(我有4個核心)這里是結果

Thread7 Value :441240435
Thread4 Value :283265150
Thread2 Value :427759029
Thread3 Value :441240435
Thread8 Value :283265150
Thread6 Value :427759029
Thread5 Value :450884067
Thread1 Value :450884067

(它甚至沒有按順序執行SOP)

public class SimpleTask implements Runnable {
    private long value = 0;
    private volatile static int oneSecondCompleted = 0;

    public SimpleTask(long lValue) {
        this.value = lValue;
    }
    @Override
    public void run() {
        while(oneSecondCompleted <= 1){
            if (oneSecondCompleted == 1){
                while (oneSecondCompleted == 1){
                    this.value++;
                }
            }
        }
        System.out.println(Thread.currentThread().getName() +" Value :"+value);
    }

    public static void main(String args[]){

        SimpleTask task1 = new SimpleTask(0);
        SimpleTask task2 = new SimpleTask(0);
        SimpleTask task3 = new SimpleTask(0);
        SimpleTask task4 = new SimpleTask(0);

        Thread thread1 = new Thread(task1,"Thread1");
        Thread thread2 = new Thread(task2,"Thread2");
        Thread thread3 = new Thread(task3,"Thread3");
        Thread thread4 = new Thread(task4,"Thread4");

        thread1.start();thread2.start();thread3.start();thread4.start();

        oneSecondCompleted = 1;
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        oneSecondCompleted = 2;
    }
}

看看你何時啟動你的計時器:你給每個SeparateExecution對象一秒鍾的數量盡可能高,但是從一開始就有一秒鍾?

第一個對象的一秒包括創建所有四個SeparateExecution對象所需的時間加上創建所有四個線程所需的時間,加上自己的線程啟動所需的時間。 你的最后一個對象的一秒包括創建一個SeparateExecution實例所需的時間,加上創建四個線程對象所需的時間,加上三次start()調用所花費的時間,加上它自己的線程啟動時間。 這是一個很大的障礙。


編輯:這是我的版本。 它使用本地計數器進行一次測量,使用實例變量計數器執行另一次測量,然后啟動線程(我的PC上有8個線程)同時執行instance-var測試。 當我運行它時,前兩個運行計數大致相同的數字,大約是並發運行產生的計數的1.5倍。

public class Threadly {
    public static void main(String[] args) throws InterruptedException {
        doLocalVar();
        doInMainThread();
        doMultiThreaded();
    }

    private static void doLocalVar() {
        long count = 0L;
        long startTime = System.currentTimeMillis();
        long endTime = startTime;
        while (endTime - startTime < 1000L) {
            count += 1L;
            endTime = System.currentTimeMillis();
        }
        System.out.println("local: elapsed=" + (endTime - startTime) + ", count=" + count);
    }

    private static void doInMainThread() {
        Spinner spinner = new Spinner("main");
        spinner.run();
        spinner.show();
    }

    private static void doMultiThreaded() throws InterruptedException {
        int numThreads = Runtime.getRuntime().availableProcessors();
        Thread[] threads = new Thread[numThreads];
        Spinner[] spinners = new Spinner[numThreads];
        for (int i=0 ; i<numThreads ; i++) {
            spinners[i] = new Spinner("t-" + Integer.toString(i));
            threads[i] = new Thread(spinners[i]);
        }
        for (Thread thread : threads)
            thread.start();
        for (Thread thread : threads)
            thread.join();
        for (Spinner spinner : spinners)
            spinner.show();
    }

    private static class Spinner implements Runnable {
        private long startTime;
        private long endTime;
        private long count;
        private final String name;

        Spinner(String name) {
            this.name = name;
        }

        // @Override
        public void run() {
            count = 0L;
            startTime = System.currentTimeMillis();
            endTime = startTime;
            while (endTime - startTime < 1000L) {
                count += 1L;
                endTime = System.currentTimeMillis();
            }
        }

        void show() {
            System.out.println(name + ": elapsed=" + (endTime - startTime) + ", count=" + count);
        }
    }
}

系統時間的內核可能只響應一個呼叫並將其他呼叫排隊。 例如,如果它在某處有同步呼叫。 由於它的性能是限制因素,因此您要測量可以調用System.currenttime()的次數,這兩種情況大致相同。 剩下的差異歸結為上下文切換和維護線程的一般開銷。

嘗試在while循環中進行查看,因此它只檢查每千次添加一次的時間條件。 這應該足以使System.current時間不再是限制因素。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM