最直白的方式直面Java多執行緒

lightTrace發表於2018-08-30

毫無疑問,網上關於多執行緒的講解很多,各種概念也很複雜,我們該怎麼去理解多執行緒?該如何最直觀的面對它,先看一段簡單的程式碼:

package cn.com;

public class TestMainNoRunnable {
  public static void main(String[] args) {
        long startTime=System.currentTimeMillis();   //獲取開始時間
        double sum = 0;
        for(int i=0;i<2000000000;i++){
            sum += i;
        }
        System.out.println(sum);
        long endTime=System.currentTimeMillis(); //獲取結束時間
        System.out.println("程式執行時間: "+(endTime-startTime)+"ms");
    }
}

這段程式碼做的事情是從1到2000000000的累加,最終輸出:

1.99999999806711398E18
程式執行時間: 2761ms

好,那麼我們當然可以將這段累加拆分到兩個執行緒去執行,然後將兩個執行緒的結果合併。

執行緒1:從0累加到800000000

package cn.com;

import java.util.concurrent.CountDownLatch;

public class TestRunnable1 implements Runnable{

      double sum1 = 0;

       @Override 
       public void run(){
            for(int i =0;i<800000000;i++){
                sum1 += i ;
            }
        }
}

執行緒2:從800000000累加到2000000000:

package cn.com;

import java.util.concurrent.CountDownLatch;

public class TestRunnable2 implements Runnable{

       double sum2 = 0;
       @Override 
       public void run(){
            for(int i =800000000;i<2000000000;i++){
                sum2 += i ;
            }
        }
}

主執行緒合併執行緒一和執行緒二的結果:

package testWSO2;

import java.util.concurrent.CountDownLatch;

public class TestRunnaleMain {

    public static void main(String[] args) throws InterruptedException {
        long startTime = System.currentTimeMillis(); // 獲取開始時間
        TestRunnable1 t1 = new TestRunnable1();
        TestRunnable2 t2 = new TestRunnable2();

        Thread t1s = new Thread(t1);
        t1s.start();

        Thread t2s = new Thread(t2);
        t2s.start();
        t2s.join();//待需要執行時間較長的t2執行緒結束再繼續執行主執行緒,否則主執行緒結束了,t1和t2還沒計算完
        System.out.println("sum1= " + t1.sum1);
        System.out.println("sum2= " + t2.sum2);
        double sum =t1.sum1+t2.sum2;
        System.out.println("sum3= " + sum);
        long endTime = System.currentTimeMillis(); // 獲取結束時間        
        System.out.println("程式執行時間: " + (endTime - startTime) + "ms");
    }
}

最終輸出:

sum1= 3.1999999926710899E17
sum2= 1.67999999880559053E18
sum3= 1.99999999807269939E18
程式執行時間: 1753ms

可以發現比直接累加1到2000000000節省了近一秒時間,大家這麼一看是不是很直觀的感受到兩個多執行緒的好處了。

大家可以改成1到200的累加,會發現單執行緒會比兩個執行緒還快,而且執行緒越多這裡越慢,這裡就可以理解了多執行緒上下文的切換也需要時間,所以一個cpu能執行的搓搓有餘何必去切換多執行緒呢?就是說有時候並不是多執行緒就好,要合理根據自己的業務來設計多執行緒。

其中 t2s.join()是為了讓執行時間較長的t2執行緒結束再繼續執行主執行緒,否則主執行緒結束了,t1和t2還沒計算完,這裡並不是最好的辦法,推薦使用Java自帶的CountDownLatch計數器來解決這個問題。

相關文章