并发(十六):不要使用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多倍,下面是成绩对比:

斐波那契数NJAVA(ms)Python(ms)结果
353129919227465
4036233533102334155

当然用递归求斐波那契数的方法绝对不是一种高效的方法,但能否说明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()方法。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值