Одновременное выполнение двух задач в Java

Я хочу выполнить эти два фрагмента кода одновременно. Вот код, который у меня есть:

@Path("/cases")
public class CaseResource {

    @GET
    @Path("/getCaseNumber")
    @Produces(MediaType.TEXT_PLAIN)
    public String getNextCaseNumber(
            @ApiParam(value = "tenant id", required = true)
            @HeaderParam("tenant_id") String tenantId) throws Exception {

        //Piece #1
        String caseNum1 = new CaseHelper(new ConfigurationService(),new CaseService()).getNextCaseNumberFromDatabase(tenantId);
        Case tempCase = new Case();
        tempCase.setCaseStatusCode(new CodeService().getCodeForKeyGroup("ACTIVE","CASE_STATUS"));
        caseService.saveCase(tempCase, tenantId);

        //Piece #2 
        String caseNum2= new CaseHelper(new ConfigurationService(),new CaseService()).getNextCaseNumberFromDatabase(tenantId);




        String caseNumbers = "{case1: " + caseNum1 + ", case2:" + caseNum2 + "}";
        return caseNumbers;
    }
}

Здесь все работает, но я хочу одновременно выполнять следующие задачи:

Задача1: вывести caseNum1, сохранить новое наблюдение в базе данных

Задача2: вывести caseNum2

Вот что я пытался сделать:

@Path("/cases")
public class CaseResource {
    String  caseNum1;
    String caseNum2;


    @GET
    @Path("/getCaseNumber")
    @Produces(MediaType.TEXT_PLAIN)
    public String getNextCaseNumber(
            @ApiParam(value = "tenant id", required = true)
            @HeaderParam("tenant_id") final String tenantId) throws Exception {

        new Thread(new Runnable() {
            public void run() {
                caseNum1= new CaseHelper(new ConfigurationService(),new CaseService()).getNextCaseNumberFromDatabase(tenantId);
                Case tempCase = new Case();
                tempCase.setCaseStatusCode(new CodeService().getCodeForKeyGroup("ACTIVE","CASE_STATUS"));
                caseService.saveCase(tempCase, tenantId);
            }
        }).start();

        new Thread(new Runnable() {
            public void run() {
                caseNum2 = new CaseHelper(new ConfigurationService(),new CaseService()).getNextCaseNumberFromDatabase(tenantId);

            }
        }).start();

        String caseNumbers = "{case1: " + caseNum1 + ", case2:" + caseNum2  + "}" ;

        return caseNumbers;
    }
}

Но caseNum1 и caseNum2 возвращают null. Есть идеи, почему? Возможно, run () вызывается неправильно. Хотя, я даже не уверен, правильно ли я делаю эту потоковую передачу. Любые идеи?


person Jay266    schedule 20.01.2016    source источник
comment
Для запуска потоков в Точно одновременно посмотрите это   -  person Dan    schedule 20.01.2016
comment
Похоже, вы собираетесь запустить это как веб-службу. Возможно, вы захотите пересмотреть использование переменных экземпляра, так как у вас может быть несколько одновременных запросов.   -  person user1675642    schedule 20.01.2016


Ответы (5)


Попробуй это:

    ...

    CountDownLatch latch = new CountDownLatch(2);


    new Thread(new Runnable() {
        public void run() {
            caseNum1= new CaseHelper(new ConfigurationService(),new CaseService()).getNextCaseNumberFromDatabase(tenantId);
            Case tempCase = new Case();
            tempCase.setCaseStatusCode(new CodeService().getCodeForKeyGroup("ACTIVE","CASE_STATUS"));
            caseService.saveCase(tempCase, tenantId);
            latch.countDown();
        }
    }).start();

    new Thread(new Runnable() {
        public void run() {
            caseNum2 = new CaseHelper(new ConfigurationService(),new CaseService()).getNextCaseNumberFromDatabase(tenantId);
            latch.countDown();

        }
    }).start();

    latch.await();

    ...
person David Soroko    schedule 20.01.2016
comment
Код по-прежнему не является потокобезопасным, поскольку переменные не изменяются / не синхронизируются, поэтому это не гарантируется. - person user1675642; 20.01.2016
comment
Неправда - CountDownLatch предоставляет необходимые гарантии. См. docs.oracle.com/javase/8/docs/api/java/util/concurrent/ - person David Soroko; 20.01.2016
comment
Ты прав. Возможно, стоит упомянуть об этом прямо. - person user1675642; 20.01.2016
comment
Возможно, с другой стороны, все конструкции, координирующие потоки в util.concurrent, похожи на это. - person David Soroko; 20.01.2016

Пара проблем, которые я вижу:

  1. Ваш код не является потокобезопасным. Нет гарантии, что записи из любого из создаваемых вами потоков будут видны в родительском потоке. Вам нужно синхронизировать то, что вы делаете, или сделать поля нестабильными
  2. Выполнение продолжается после вызова start () в потоке. Поток выполняется параллельно. Следовательно, в вашем коде есть состояние гонки между записью в дочернем потоке и чтением в родительском потоке. Вам нужна координация между родительским и дочерним потоками, например, путем вызова для них join ().
person user1675642    schedule 20.01.2016

Вам нужно дождаться завершения потока, иначе код будет запущен до того, как поток заполнит значения. Когда вы запускаете новый поток, вычисления продолжаются, и порядок выполнения неизвестен. Таким образом, ваш основной поток может получить результат до того, как t1 и t2 фактически вычислили значения.

 Thread t1 = new Thread(new Runnable() {
       public void run() {
                caseNum1= new CaseHelper(new ConfigurationService(),new CaseService()).getNextCaseNumberFromDatabase(tenantId);
                Case tempCase = new Case();
                tempCase.setCaseStatusCode(new CodeService().getCodeForKeyGroup("ACTIVE","CASE_STATUS"));
                caseService.saveCase(tempCase, tenantId);
            }
        });

 t1.start();

 Thread t2 = new Thread(new Runnable() {
        public void run() {
            caseNum2 = new CaseHelper(new ConfigurationService(),new CaseService()).getNextCaseNumberFromDatabase(tenantId);

        }
    });
t2.start();

t1.join();
t2.join();

String caseNumbers = "{case1: " + caseNum1 + ", case2:" + caseNum2  + "}" ;
person Simone Zandara    schedule 20.01.2016
comment
Когда я пробую Thread t1 = new Thread (new Runnable (), он говорит, что для него требуется java.lang.Thread, и он обнаружил пустоту. Есть идеи? - person Jay266; 20.01.2016
comment
Извините, я не заметил начало () в конце. Сначала создайте объект потока, затем запустите их. - person Simone Zandara; 20.01.2016

Вы должны посмотреть на асинхронные операции сервлета 3.0. В этом случае вы можете отложить фиксацию ответа до тех пор, пока не завершите асинхронную операцию.

См. Этот блог весной: https://spring.io/blog/2012/05/07/spring-mvc-3-2-preview-introduction-servlet-3-async-support

person Paul Arer    schedule 20.01.2016

Я думаю, что хороший подход - использовать Feature. В приведенном ниже примере вы запускаете две задачи в параллельных потоках, и у вас все еще есть рабочее исключение, как и в вашем однопоточном примере.

public String getNextCaseNumber(String tenantId) throws Exception {
    ExecutorService executor = Executors.newFixedThreadPool(2);
    Future<String> caseNum1 = executor.submit(() -> {
        String caseNum = new CaseHelper(new ConfigurationService(), new CaseService()).getNextCaseNumberFromDatabase(tenantId);
        Case tempCase = new Case();
        tempCase.setCaseStatusCode(new CodeService().getCodeForKeyGroup("ACTIVE", "CASE_STATUS"));
        caseService.saveCase(tempCase, tenantId);
        return caseNum;
    });
    Future<String> caseNum2 = executor.submit(() -> new CaseHelper(new ConfigurationService(), new CaseService()).getNextCaseNumberFromDatabase(tenantId));

    return String.format("{case1: %s, case2: %s}", caseNum1.get(), caseNum2.get());
}
person oleg.cherednik    schedule 01.02.2017