我正在使用由TaskExecutor
执行的线程在Spring Boot项目中工作。据我所知,@Scope("singleton")
意味着如果我希望Spring返回相同的bean实例,那么如果我在带有@Component
注释的线程上声明它,Spring将仅返回该线程。当我尝试通过TaskExecutor多次执行该线程时,我认为它每次应返回相同的线程ID,但似乎返回不同的结果。有人可以帮我解释一下吗?
@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
答案 0 :(得分:0)
我认为,如您在问题中所述,可以更好地解释单例的概念:
有一个应用程序上下文-如果需要,可以显示所有spring bean的全局映射。
现在,Singleton意味着每次您请求bean(就像您直接调用applicationContext.getBean
一样,或者如果spring本身是为了注入而这样做的),都将返回该对象的相同实例。它与不同的线程无关。
换句话说, 如果您运行多个线程并要求应用程序上下文获取单例bean,它将始终返回相同的实例。
对于原型来说,与众不同,总是会创建一个新实例。
因此,如果此实现:
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可以完美地在多线程环境中工作,例如,控制器可以由不同的客户端同时调用(因此,不同的线程,因为每个请求模型都是线程)。
这与多线程无关,而与注入有关。
答案 1 :(得分:0)
@BoristheSpider的希望评论消除了您对Thread和Runnable的怀疑。
关于单例,this answer将帮助您了解更多信息。
我将尝试回答OP的声明
当我多次尝试通过TaskExecutor执行该线程时,我 认为它应该每次都返回相同的线程ID,但是似乎 返回不同的结果。有人可以帮我解释一下吗?
就像我们使用不同的帮助程序来完成某些工作一样,这里的帮助程序是线程,工作是您的业务逻辑(在MyThread中)。
假设我们只有一名助手来完成我们的任务,这将花费大约10秒钟的时间,而我们需要做3次此工作。
但是由于我们只有1名工人,因此完成任务至少需要10 + 10 + 10 = 30s。
在下面的测试类中,我增加了30s的睡眠时间,以便所有子线程都可以在父线程完成执行之前完成其工作。
OP中的MyThread.java
添加了更多日志和睡眠。
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,因此同一线程将用于执行所有三个调用。
如果将线程总数更改为2,则一次将使用两个线程来执行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]
答案 2 :(得分:-3)
我写了如何创建@Column
的答案,这意味着您应该查看有关此行的源代码
Thread
taskExecutor.execute(myThread);
在您的代码名“ 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,**
}
,Spring仅包含那个Bean,但是它仅提供了一个Runnable
方法,
因此单个 bean只是一种方法而使您的代码崩溃。并不意味着线程池中的线程是唯一的
run()
这个问题是: 如何创建线程
扩展@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());
}}
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();
}
}
,runnable不是线程,可以由
许多线程。我的英语不好,希望我能帮到你
Runnable
}
以下是Thread.class源代码:
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!");
}
@Override
public void run() {
if (target != null) {
target.run();//the target is a runnable
}
}
接口,并将其与Callable
和Future
一起使用。它将返回每个线程的结果。如果您想学习,可以用Google搜索