【问题标题】:Java multithreading inner class calling outer class [duplicate]Java多线程内部类调用外部类[重复]
【发布时间】:2021-11-28 19:57:28
【问题描述】:

我有一个实现可运行的内部类,它发出 HTTP 请求,然后调用外部类的函数来处理响应。预期的行为是将所有响应附加到对象列表。

示例代码:

public class Status {

private ExecutorService executor;
CloseableHttpClient httpClient;
List<String> results = new LinkedList<>();

public Status() {
    executor = Executors.newFixedThreadPool(5);
    httpClient = HttpClients.createDefault();
}


public handleInput(Entity entity) {
   String result = EntityUtils.toString(httpEntity);
   results.add(result); 
}

private class Action implements Runnable {

    @Override
    public void run() {
        try {
            //do some http calls
            // response = httpClient.execute(httpPost);
            handleInput(response.getEntity())
        } catch (SomeException e) {
            lastResult = false;

        }

    }
}}

在我的测试过程中,我没有遇到任何问题,但我想知道将来自多个线程的结果添加到同一个链表是否是线程安全操作,如果不是,那么这种情况下的最佳设计是什么。

【问题讨论】:

  • 这里的内部类和普通类没有区别,将handleInput 设为synchronized 方法。
  • 一般来说,基于集合的类不是线程安全的。您可以使用 Collections.syncronizedXxx 将实例包装在线程安全包装器中,或者使用您自己的 synchronized 块或工作流
  • 忽略线程安全,你为什么要使用LinkedList?

标签: java multithreading http inner-classes outer-classes


【解决方案1】:

不是线程安全的

不,跨线程操作非线程安全的List 不是thread-safe

Synchronized

您可以将handleInput 方法synchronized 设为commented by Bodewes。当然,该方法成为潜在的瓶颈,因为一次只有一个线程可以调用同步方法。在您的情况下可能不是问题,但请注意。

线程安全的集合

另一种选择是将您的LinkedList 替换为线程安全的集合。例如,CopyOnWriteArrayListCopyOnWriteArraySetConcurrentSkipListSet

Callable & Future

但我建议您不要将Entity 对象的多线程生产与收集和处理这些对象混为一谈。分配给后台线程的每个任务都应该尽可能地“管好自己的事”。让任务共享一个列表会使它们不必要地跨线程纠缠。应尽可能避免这种纠缠(共享资源)。

将您的任务从 Runnable 更改为 Callable 以便返回一个值。该返回值将由每个Entity 生成。

当您将每个Callable 提交给执行器服务时,您会返回一个Future 对象。收集那些物品。通过这些对象中的每一个,您都可以访问每个任务的工作结果。

等待执行器服务完成所有提交的任务。然后检查每个Future的结果。

通过使用Future 对象来收集后台线程产生的结果,并且只处理那些结果它们都完成之后,我们就不需要让你的results 收集线程-安全的。原始线程将这些结果收集到一个列表中,而不是每个线程都添加到列表中。

请注意,在下面的示例代码中,我们没有在后台线程中运行的任务之间共享资源。每个任务都做自己的事情,通过其特定的Future 对象报告自己的结果。这些任务不再访问共享的List

顺便说一下,请注意,此示例代码没有像问题中的代码那样将执行程序服务保留在成员字段中。在我们这里的情况下,执行器服务应该 (1) 实例化,(2) 使用,以及 (3) 一次性关闭所有操作。您必须在不再需要时(或应用退出时)关闭执行器服务。否则它的后备线程池可能会像僵尸一样无限期地运行?‍♂️。

示例代码

package work.basil.threading;

import java.net.URI;
import java.net.URISyntaxException;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.*;

public class Status
{
    record Entity( UUID uuid , Instant instant ) { }

    public List < String > process ()
    {
        ExecutorService executorService = Executors.newFixedThreadPool( 5 );

        List < WebCallTask > tasks = List.of();
        try
        {
            tasks = List.of(
                    new WebCallTask( new URI( "http://www.Google.com/" ) ) ,
                    new WebCallTask( new URI( "http://www.DuckDuckGo.com/" ) ) ,
                    new WebCallTask( new URI( "http://www.Adoptium.net/" ) )
            );
        } catch ( URISyntaxException e )
        {
            e.printStackTrace();
        }

        List < Future < Entity > > futures = List.of();
        try { futures = executorService.invokeAll( tasks ); } catch ( InterruptedException e ) { e.printStackTrace(); }

        executorService.shutdown();
        try { executorService.awaitTermination( 2 , TimeUnit.MINUTES ); } catch ( InterruptedException e ) { e.printStackTrace(); }

        List < String > results = new ArrayList <>( tasks.size() );
        for ( Future < Entity > future : futures )
        {
            try
            {
                Entity entity = future.get();
                String result = this.handleInput( entity );
                results.add( result );
            } catch ( InterruptedException e )
            {
                e.printStackTrace();
            } catch ( ExecutionException e )
            {
                e.printStackTrace();
            }
        }
        return results;
    }

    public String handleInput ( Entity entity )
    {
        if ( Objects.isNull( entity ) ) return "Not Available.";
        return entity.toString();
    }

    private class WebCallTask implements Callable < Entity >
    {
        private URI uri;

        public WebCallTask ( URI uri )
        {
            this.uri = uri;
        }

        @Override
        public Entity call ()
        {
            Entity entity = null;
            try
            {
                // Perform some http calls.
                // response = httpClient.execute(httpPost);
                // Pretend to wait on network call by sleeping.
                System.out.println( "Thread: " + Thread.currentThread().getId() + " is sleeping, to pretend doing network call. " + Instant.now() );
                try { Thread.sleep( Duration.ofSeconds( ThreadLocalRandom.current().nextInt( 3 , 11 ) ).toMillis() ); } catch ( InterruptedException e ) { e.printStackTrace(); }
                entity = new Entity( UUID.randomUUID() , Instant.now() );
                System.out.println( "Thread: " + Thread.currentThread().getId() + " produced an `Entity` object. Task done. " + Instant.now() );
            } catch ( Exception e )  // In your real code, you would be catching networking errors related to your networkcall.
            {
                e.printStackTrace();
            } finally
            {
                return entity;  // May return `null` as a legitimate value. In real work I would use `Optional< Entity >` here to signal that `null` is a possible and legitimate value. But let's not overcomplicate this example code.
            }
        }
    }

    public static void main ( String[] args )
    {
        System.out.println( "Thread: " + Thread.currentThread().getId() + " is starting demo. " + Instant.now() );

        Status statusApp = new Status();
        List < String > output = statusApp.process();
        System.out.println( "output = " + output );

        System.out.println( "Thread: " + Thread.currentThread().getId() + " is ending demo. " + Instant.now() );
    }

}

运行时。

Thread: 1 is starting demo. 2021-10-09T03:58:41.269177Z
Thread: 15 is sleeping, to pretend doing network call. 2021-10-09T03:58:41.286424Z
Thread: 16 is sleeping, to pretend doing network call. 2021-10-09T03:58:41.286828Z
Thread: 17 is sleeping, to pretend doing network call. 2021-10-09T03:58:41.288108Z
Thread: 16 produced an `Entity` object. Task done. 2021-10-09T03:58:44.323703Z
Thread: 15 produced an `Entity` object. Task done. 2021-10-09T03:58:46.294364Z
Thread: 17 produced an `Entity` object. Task done. 2021-10-09T03:58:46.294269Z
output = [Entity[uuid=04d73a52-79ec-4a61-becb-ce056d3aa9fa, instant=2021-10-09T03:58:46.294359Z], Entity[uuid=cc5a7266-4101-41bb-b806-8b29b77a82d0, instant=2021-10-09T03:58:44.323688Z], Entity[uuid=3cc24ad9-3ea1-4a24-98d0-c3df4bf161b6, instant=2021-10-09T03:58:46.294254Z]]
Thread: 1 is ending demo. 2021-10-09T03:58:46.321313Z

【讨论】:

    猜你喜欢
    • 2011-02-18
    • 1970-01-01
    • 2014-08-21
    • 1970-01-01
    • 2023-01-24
    • 2014-07-17
    • 2015-11-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多