@Scope ("singleton") с идентификатором потока в приложении Spring Boot - PullRequest
0 голосов
/ 05 июня 2019

Я работаю над проектом Spring Boot с потоком, выполняемым TaskExecutor. Насколько я знаю, @Scope("singleton") означает, что если я хочу, чтобы Spring возвращал тот же экземпляр компонента, поэтому, если я объявлю его в потоке, помеченном @Component, Spring вернет только этот поток. Когда я пытался выполнить этот поток через TaskExecutor много раз, я думаю, что он должен возвращать один и тот же идентификатор потока каждый раз, но, похоже, он возвращает разные результаты. Может кто-нибудь объяснить это для меня?

@Component
@Scope("singleton")
public class MyThread implements Runnable {

    private static final Logger LOGGER = LoggerFactory.getLogger(MyThread.class);

    @Override
    public void run() {
        LOGGER.info("Called from thread + " + Thread.currentThread().getId());
    }
}

У меня есть служба для выполнения вышеуказанного потока:

@Service
public class AsynchronousService {

    @Autowired
    private TaskExecutor taskExecutor;

    @Autowired
    private ApplicationContext applicationContext;

    public void executeAsynchronously() {
        MyThread myThread = applicationContext.getBean(MyThread.class);
        taskExecutor.execute(myThread);
    }

Мой файл конфигурации:

@Configuration
@EnableAsync
public class ThreadConfig {

    @Bean
    @Primary
    public TaskExecutor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(4);
        executor.setMaxPoolSize(4);
        executor.setThreadNamePrefix("default_task_executor_thread");
        executor.initialize();
        return executor;
    }

Тогда у меня есть контроллер:

@RestController
public class HelloController {

    @Autowired
    private AsynchronousService asynchronousService;

    @RequestMapping("/runTask")
    public String executeAsync() {
        asynchronousService.executeAsynchronously();
        return "OK";
    }

Вот результаты:

2019-06-05 11:48:21.019  INFO 4056 --- : Called from thread + 97
2019-06-05 11:48:22.932  INFO 4056 --- : Called from thread + 101
2019-06-05 11:48:23.119  INFO 4056 --- : Called from thread + 65
2019-06-05 11:48:23.372  INFO 4056 --- : Called from thread + 93
2019-06-05 11:48:23.531  INFO 4056 --- : Called from thread + 97
2019-06-05 11:48:23.799  INFO 4056 --- : Called from thread + 101
2019-06-05 11:48:23.961  INFO 4056 --- : Called from thread + 65

Ответы [ 3 ]

0 голосов
/ 05 июня 2019

Я думаю, что концепция синглтона может быть лучше объяснена тем, что вы сформулировали в вопросе:

Существует контекст приложения - глобальная карта всех компонентов Spring, если хотите.

Теперь Singleton означает, что каждый раз, когда вы запрашиваете бин (как вы это делали, напрямую вызывая applicationContext.getBean или если сама Spring делает это для инъекции), будет возвращаться один и тот же экземпляр объекта.Это не имеет ничего общего с разными темами.

Другими словами, если вы запустите много потоков и попросите контекст приложения получить одноэлементный компонент, он всегда будет возвращать один и тот же экземпляр.

Для прототипа, который отличается от него, всегда будет новый экземплярсоздал.

Итак, если эта реализация:

public class MyThread implements Runnable {

private static final Logger LOGGER = LoggerFactory.getLogger(MyThread.class);

   @Override
   public void run() {
      LOGGER.info("Called from thread + " + Thread.currentThread().getId());
   }
}

Попробуйте сделать что-то подобное для проверки, и вы поймете, о чем я говорю:

 public class MyThread2 implements Runnable {
    private Object random = //  createRandomLong // or UUID or whatever
    private static final Logger LOGGER = ...
    public void run() {
        LOGGER.info("Called the bean: " + random);      
    }        
 }

Теперь запустите его из разных потоков (и вы увидите его один и тот же экземпляр)

Теперь вообще Spring может отлично работать в многопоточных средах, например, контроллеры могут вызываться одновременно разными клиентами (следовательно, разнымипотоки, так как это модель потока по запросу)

Дело не в многопоточности, а в инжекции.

0 голосов
/ 05 июня 2019

Надеюсь, что комментарий @BoristheSpider снял ваши сомнения относительно Thread и Runnable.
Относительно синглтона, этот ответ поможет вам понять больше.
Я постараюсь ответить на утверждение от OP

Когда я пытался выполнить этот поток через TaskExecutor много раз, я думаю, что он должен возвращать один и тот же идентификатор потока каждый раз, но, похоже, он возвращает разные результаты.Может ли кто-нибудь объяснить это для меня?

Это похоже на то, что мы используем различных помощников для выполнения какой-то работы, здесь помощники являются потоками, а работа - вашей бизнес-логикой (в MyThread).
Допустим, мыу нас будет только 1 помощник для выполнения нашей задачи, и это займет около 10 секунд времени, и нам нужно выполнить эту работу 3 раза.
Но так как у нас есть только 1 работник, это займет минимум 10 + 10 + 10 =30 секунд, чтобы выполнить свою задачу.
В следующем тестовом классе я добавил сон 30-х годов, чтобы все дочерние потоки могли завершить свою работу до того, как родительские потоки закончат выполнение.
MyThread.java из OP
Добавлены еще некоторыежурналы и сон.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("singleton")
public class MyThread implements Runnable {

    private static final Logger LOGGER = LoggerFactory.getLogger(MyThread.class);

    @Override
    public void run() {
        LOGGER.info("Called from thread + " + Thread.currentThread().getId());
        LOGGER.info("Thread info+ " + Thread.currentThread().getName());
        LOGGER.info("Thread info+ " + Thread.currentThread().getThreadGroup());
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

ThreadConfig.java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@EnableAsync
@Configuration
public class ThreadConfig {
    @Bean(name = "threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(1);
        executor.setMaxPoolSize(1);
        executor.setThreadNamePrefix("default_task_executor_thread");
        executor.initialize();
        return executor;
    }
}

Тестовый класс

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class DemoApplicationTests {
    @Autowired
    ApplicationContext applicationContext;

    @Autowired
    Executor threadPoolTaskExecutor;

    @Test
    public void test() throws InterruptedException {
        MyThread myThread = applicationContext.getBean(MyThread.class);
        if (threadPoolTaskExecutor != null) {
            threadPoolTaskExecutor.execute(myThread);
            threadPoolTaskExecutor.execute(myThread);
            threadPoolTaskExecutor.execute(myThread);
            Thread.sleep(30000);// 10000 + 10000 + 10000 ^^ for each thread
        }
    }
}

Вывод:

2019-06-05 12:31:01.549  INFO 68118 --- [xecutor_thread1] com.example.controller.MyThread          : Called from thread + 22
2019-06-05 12:31:01.549  INFO 68118 --- [xecutor_thread1] com.example.controller.MyThread          : Thread info+ default_task_executor_thread1
2019-06-05 12:31:01.549  INFO 68118 --- [xecutor_thread1] com.example.controller.MyThread          : Thread info+ java.lang.ThreadGroup[name=main,maxpri=10]
2019-06-05 12:31:11.552  INFO 68118 --- [xecutor_thread1] com.example.controller.MyThread          : Called from thread + 22
2019-06-05 12:31:11.552  INFO 68118 --- [xecutor_thread1] com.example.controller.MyThread          : Thread info+ default_task_executor_thread1
2019-06-05 12:31:11.552  INFO 68118 --- [xecutor_thread1] com.example.controller.MyThread          : Thread info+ java.lang.ThreadGroup[name=main,maxpri=10]
2019-06-05 12:31:21.554  INFO 68118 --- [xecutor_thread1] com.example.controller.MyThread          : Called from thread + 22
2019-06-05 12:31:21.555  INFO 68118 --- [xecutor_thread1] com.example.controller.MyThread          : Thread info+ default_task_executor_thread1
2019-06-05 12:31:21.555  INFO 68118 --- [xecutor_thread1] com.example.controller.MyThread          : Thread info+ java.lang.ThreadGroup[name=main,maxpri=10]

Если вы проверитеПриведенная выше конфигурация, поскольку у нас есть только 1 Поток для выполнения Runnable, один и тот же Поток используется для выполнения всех трех вызовов.

Если вы измените Общее количество потоков на два, одновременно два потока будут использоваться для выполненияRunnable (MyThread) аи
как только одна из задач будет выполнена, другая задача будет использовать тот же поток, который был освобожден ранее выполняемым.

executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);

ВЫХОД:

2019-06-05 12:39:26.163  INFO 68407 --- [xecutor_thread2] com.example.controller.MyThread          : Called from thread + 23
2019-06-05 12:39:26.163  INFO 68407 --- [xecutor_thread2] com.example.controller.MyThread          : Thread info+ default_task_executor_thread2
2019-06-05 12:39:26.163  INFO 68407 --- [xecutor_thread2] com.example.controller.MyThread          : Thread info+ java.lang.ThreadGroup[name=main,maxpri=10]
2019-06-05 12:39:26.163  INFO 68407 --- [xecutor_thread1] com.example.controller.MyThread          : Called from thread + 22
2019-06-05 12:39:26.164  INFO 68407 --- [xecutor_thread1] com.example.controller.MyThread          : Thread info+ default_task_executor_thread1
2019-06-05 12:39:26.164  INFO 68407 --- [xecutor_thread1] com.example.controller.MyThread          : Thread info+ java.lang.ThreadGroup[name=main,maxpri=10]
2019-06-05 12:39:36.169  INFO 68407 --- [xecutor_thread1] com.example.controller.MyThread          : Called from thread + 22
2019-06-05 12:39:36.169  INFO 68407 --- [xecutor_thread1] com.example.controller.MyThread          : Thread info+ default_task_executor_thread1
2019-06-05 12:39:36.169  INFO 68407 --- [xecutor_thread1] com.example.controller.MyThread          : Thread info+ java.lang.ThreadGroup[name=main,maxpri=10]
0 голосов
/ 05 июня 2019

Я пишу ответ, как создать Thread, я имею в виду, вы должны посмотреть исходный код об этой строке

taskExecutor.execute(myThread);

    Worker(Runnable firstTask) {
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);//**here newThread create a Thread to the Thread pool,**
    }

в вашем кодовом имени "MyThread"это всего лишь Runnable, Spring содержит только один Bean, но он просто предоставляет метод run(), поэтому single bean выдает ваш код всего лишь методом.Не означает, что поток в пуле потоков является уникальным

@Component
@Scope("singleton")
public class MyThread implements Runnable {

    private static final Logger LOGGER = LoggerFactory.getLogger(MyThread.class);

    @Override
    public void run() {
        LOGGER.info("Called from thread + " + Thread.currentThread().getId());
    }}

Этот вопрос: Как создать поток

  1. extension Thread

    public class NewThread1 extends Thread {
        private String name;
        public NewThread1(String name) {
            this.name=name;
        }
        public void run() {
            for (int i = 0; i < 5; i++) {
                System.out.println(name + "running  :  " + i);
                try {
                    sleep((int) (Math.random() * 10));
                } catch (InterruptedException e) {
                    e.printStackTrace();
    
                }
            }
        }
    public static void main(String[] args){
        NewThread1 mTh1=new NewThread1("A");
        NewThread1 mTh2=new NewThread1("B");
        mTh1.start();
        mTh2.start();
      }
    }
    
  2. При использовании Runnable, runnable не является потоком, он может использоваться многими потоками.мой английский плохой, я хотел бы помочь вам

    public class MyRunnable implements Runnable{
    private String name;
    MyRunnable(String name){
        this.name=name;
    }
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(name + "running  :  " + i);
            try {
                Thread.sleep((int) (Math.random() * 10));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        System.out.println("main thread start running!");
        Thread thread1=new Thread(new MyRunnable("A"));
        thread1.start();
        thread1.join();
        System.out.println("main thread now is End My dear!");
    }
    

    }

ниже приведен исходный код Thread.class:

    @Override
    public void run() {
        if (target != null) {
            target.run();//the target is a runnable
        }
    }
Фактически, должен быть третий, и один должен реализовать интерфейс Callable и использовать его с Future и thread pool.он вернет результат каждого потока.Вы можете погуглить, если хотите узнать
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...