必威体育Betway必威体育官网
当前位置:首页 > IT技术

并发(十六):不要使用Thread.join()——并行变串行

时间:2019-08-06 14:43:12来源:IT技术作者:seo实验室小编阅读:61次「手机版」
 

thread.join

在多线程程序的编写中,为了同步线程的执行状态,我们为了方便,经常会使用thread.join()方法,须不知此方法有重大的性能缺陷,能将多线程程序变成单线程程序,执行时间瞬间翻倍,示例如下:

/**
 *  用于长时间的任务计算,一般求fabic(40)就会花费1秒的时间
 *  花费时间呈指数增长速度
 */
static long fabic(int n) {
    if(n < 0) {
        throw new numberformatexception("不能小于0");
    }
    if(n == 1 || n == 2) {
        return 1;
    }
    return fabic(n - 1) + fabic(n - 2);
}

public static void main(String[] args) throws InterruptedException {
    long startTime = system.currenttimemillis();
    int NUM = 3;
    for(int i = 0; i < NUM; i ++) {
        //  创建线程
        Thread th = new Thread(() -> {
            fabic(35 + i);
        });
        th.start();
        th.join();
    }
    //  打印花费的时间
    System.out.println(System.currentTimeMillis() - startTime);
}

通过监测应用程序的执行时间,或者在线程内添加调试日志,我们发现,现在三个线程会依次执行,即线程0执行完了才执行线程1,类似于连续调用了3次th.run()方法(其实效率更低,因为还有线程创建、切换、销毁的时间)。

在上述的代码测试中,我们还发现了一个令人惊讶的现象,调用fabic的递归函数java的执行时间竟然超过Python 100多倍,下面是成绩对比:

斐波那契数N JAVA(ms) Python(ms) 结果
35 31 2991 9227465
40 362 33533 102334155

当然用递归求斐波那契数的方法绝对不是一种高效的方法,但能否说明Python对递归优化得不够,还是Python对栈桢的设计策略存在问题?更高效的求斐波那契数的方法如下:

#   用公式求导更快
def fabic(n):
    if(n < 1):
        raise Exception('the argument can not less than 0')
    #   辅助数组
    result = [0, 1, 1]
    if(n < 2):
        return 1
    while(n > 2):
        result.pop(0)
        result.APPend(result[0] + result[1])
        n = n - 1
    else:
        return result[2]

所以,除了在调试环境中,可以执行Thread.join()方法,否则还不如不要启动线程,直接执行方法调用,正确的方法是应该使用countdownlatch、CyclicBarrier这样的线程工具,如下:

static class FabicTask implements Runnable {

    private int num;

    private final CyclicBarrier barrier;

    public FabicTask(int num, CyclicBarrier barrier) {
        super();
        this.num = num;
        this.barrier = barrier;
    }

    public void run() {         
        fabic(num);
        try {
            //  发信号等待结束
            this.barrier.await();
        } catch (InterruptedException e) {
            e.printstacktrace();
        } catch (brokenBarrierException e) {
            e.printStackTrace();
        }
    }
}

public static void main(String[] args) throws InterruptedException {
    int NUM = 3;
    long startTime = System.currentTimeMillis();
    CyclicBarrier barrier = new CyclicBarrier(NUM, () -> {
        //  计算耗费时间
        System.out.println(System.currentTimeMillis() - startTime); 
    });
    for(int i = 0; i < NUM; i ++) {
        new Thread(new FabicTask(41, barrier)).start();
    }
}

结论

不要再生产环境中使用Thread.join()方法。

相关阅读

分享到:

栏目导航

推荐阅读

热门阅读