【问题标题】:How to check that pool 'reuses' Thread如何检查池“重用”线程
【发布时间】:2017-02-16 13:36:38
【问题描述】:

为了检查我写了代码的主题:

public class ThreadPoolTest {
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 100; i++) {
            if (test() != 5 * 100) {
                throw new RuntimeException("main");
            }
        }
        test();
    }

    private static long test() throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(100);
        CountDownLatch countDownLatch = new CountDownLatch(100 * 5);
        Set<Thread> threads = Collections.synchronizedSet(new HashSet<>());
        AtomicLong atomicLong = new AtomicLong();
        for (int i = 0; i < 5 * 100; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        threads.add(Thread.currentThread());
                        atomicLong.incrementAndGet();
                        countDownLatch.countDown();
                    } catch (Exception e) {
                        System.out.println(e);
                    }


                }
            });
        }
        executorService.shutdown();
        countDownLatch.await();
        if (threads.size() != 100) {
            throw new RuntimeException("test");
        }
        return atomicLong.get();
    }
}

如你所见,我使用HashSet&lt;Thread&gt;

只有当 Thread 不可变时,它的使用才是正确的。至少在测试中。

据我了解,equals/hashCode 未被覆盖,因此从 Object 继承。

因此,如果我的测试不正确以及错误在哪里,请回答。
如果您知道更聪明的方法,请分享。

【问题讨论】:

  • 您能进一步解释您遇到的问题吗?您的代码在我的项目中运行良好。
  • @Henrik 我不确定我是否理解线程轮询如何“重用”线程
  • 我认为您的代码实际上设法验证 test() 创建了 100 个线程并使用它们执行了 500 个小任务。如果不是这种情况,您的RunTimeExceptions 将被抛出。您是否正在寻找更短的方法来证明这一点?
  • @Henrik 讨厌这个实验很长时间,我确信这个输出保证了

标签: java multithreading threadpool


【解决方案1】:

正如我在评论中提到的,我认为您的代码实际上设法验证test() 创建了 100 个线程并使用它们执行了 500 个小任务。如果不是这种情况,您的RuntimeException 断言肯定会触发。

验证这一点的另一种方法是使用ThreadLocal 来计算实际运行的线程数。

我创建了以下单元测试,它使用ThreadLocal 来计算Executor 创建的线程数。尝试研究其输出以确信创建的线程数。

@Test
public void threadCounter() throws Throwable {
    ThreadLocal<Integer> number = new ThreadLocal<Integer>() {
        private final AtomicInteger values = new AtomicInteger();

        @Override
        protected Integer initialValue() {
            return values.getAndIncrement();
        }
    };

    ExecutorService threadPool = Executors.newFixedThreadPool(100);

    for (int i = 0 ; i < 500 ; i++) {
        final int count = i;
        threadPool.submit(() -> System.out.printf("%-4s : %-20s - %s\n", count, Thread.currentThread().getName(), number.get()));
    }

    threadPool.shutdown();
    threadPool.awaitTermination(10, TimeUnit.SECONDS);

    Assert.assertEquals(100, number.get().intValue());

}

【讨论】:

  • 我手头没有junit。通过阅读,我不明白你在第一次之后在哪里增加 threadLocal
  • 我也认为名称可以重复使用。如果第一个线程死了 - 第二个使用这个名字
  • 将代码复制到一个 'main' 方法以获得相同的效果。每当新线程第一次访问number 时,就会调用ThreadLocal 实例的initialValue。这会导致 number 递增。
  • 好主意,我的问题的第二部分 - 检查我的代码是否线程安全
【解决方案2】:

我不确定我理解线程 [pool] 如何“重用”线程的正确方式。

池线程(又名,工作线程)有一个run() 方法,就像任何其他线程一样,这就是“重用”发生的地方。基本上,工作线程的run() 方法从队列中挑选任务(即客户端提供的Runnable 对象),然后运行它们:

class ReallySimplePoolWorker {

    public ReallySimplePoolWorker(BlockingQueue<Runnable> workQueue) {
        this->workQueue = workQueue;
    }

    public void Run( ) {
        while (...not shutting down...) {
            Runnable task = workQueue.take();
            try {
                task.run();
            } catch (Exception ex) {
                ...notify the pool object of the exception...
            }
        }
    }

    private final BlockingQueue<Runnable> workQueue;
}

【讨论】:

    猜你喜欢
    • 2019-09-14
    • 2013-08-05
    • 2018-10-02
    • 2012-11-21
    • 2013-04-05
    • 2011-12-05
    • 2014-09-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多