为什么这个并发 Java 代码经常失败?

问题描述

//Initially,I wanted to compare synchronized with Lock
public class SynchronizedVSLock {
    static final Lock lock = new reentrantlock();
    static final int loopTime = 10;
    static final int numOfThread = 6;
    static final Random random = new Random();
    static final Semaphore runningThreadsNum = new Semaphore(numOfThread);
    public static void main(String[] args) throws InterruptedException {
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < numOfThread - 1; i++) {
            new Thread(new Test1()).start();
        }
        new Thread(new Test1()).join();
        runningThreadsNum.acquire(numOfThread);
        long endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime);
    }

    static class Test1 implements Runnable {
        @Override
        public void run() {
            try {
                runningThreadsNum.acquire();
            } catch (InterruptedException e) {
                throw new RuntimeException();
            }
            for (int i = 0; i < SynchronizedVSLock.loopTime; i++) {
                SynchronizedVSLock.lock.lock();
                System.out.println(SynchronizedVSLock.random.nextDouble());
                SynchronizedVSLock.lock.unlock();
            }
            runningThreadsNum.release();
        }
    }

    static class Test2 implements Runnable {
        @Override
        public void run() {
            try {
                runningThreadsNum.acquire();
            } catch (InterruptedException e) {
                throw new RuntimeException();
            }
            for (int i = 0; i < SynchronizedVSLock.loopTime; i++) {
                synchronized (SynchronizedVSLock.lock) {
                    System.out.println(SynchronizedVSLock.random.nextDouble());
                }
            }
            runningThreadsNum.release();
        }
    }
}

大体思路是创建多个线程并发执行输出随机数的任务,分别使用lock和synchronized两种同步机制。 最后输出程序运行时间作为指标。 使用信号量确保主线程在所有子线程都完成之前不会获得最终经过的时间 但是我发现经常是主线程在其他子线程运行之前就拿到了所有的权限,然后打印出非常小的运行时间,只有一两毫秒,不知道怎么回事。

解决方法

您正在加入一个您没有启动的线程,而不是等待您在循环中启动的任何线程:

for (int i = 0; i < numOfThread - 1; i++) {
   new Thread(new Test1()).start();
}
new Thread(new Test1()).join();

在未启动的线程上调用 join() 会立即返回。这意味着您的初始线程可以在第一个 Test1 线程甚至开始执行之前获得信号量的所有 6 个许可,这意味着它只会打印时间并退出。

您应该考虑在此处使用 CountDownLatch 而不是信号量,因为这是它的教科书用例。

,

您使用主程序所需的所有许可初始化您的信号量,以继续在线运行线程数.acquire(numOfThread)。

static final Semaphore runningThreadsNum = new Semaphore(0); // should be zero