[英]@Scope(“singleton”) with thread Id in Spring Boot application
我正在使用由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
我認為您可以在問題中更好地解釋單例的概念:
有一個應用程序上下文-如果需要,可以顯示所有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可以完美地在多線程環境中工作,例如,可以由不同的客戶端同時調用控制器(因此,它具有不同的線程,因為它是每個請求線程的模型)。
它與多線程無關,而與注入有關。
希望來自@BoristheSpider的評論消除了您對Thread和Runnable的懷疑。
關於單例, 此答案將幫助您了解更多。
我將嘗試回答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,因此將使用同一線程來執行所有三個調用。
如果將線程總數更改為兩個,則一次將使用兩個線程來執行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]
我寫了如何創建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()
方法,因此單個 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());
}}
這個問題是: 如何創建線程
擴展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不是一個線程,它可以被許多線程使用。 我的英語不好我希望我能幫助你
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
。 它將返回每個線程的結果。 如果你想學習,可以用谷歌搜索
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.