Spring中的線程池與任務調度

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"線程池已經成爲 Java 開發中必不可少的一個組件了,在使用 Spring 時,不需要自己重頭去使用線程池。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring 已經提供了非常完備的封裝,可以直接使用 Spring 提供的接口。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"💡本文基於 Spring5.3 和 OpenJDK11","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"1. Spring 中的任務","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring 中對與任務的執行提供了兩種抽象, ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TaskExecutor","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TaskScheduler","attrs":{}}],"attrs":{}},{"type":"text","text":",分別表示執行異步任務和定時任務。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Executor","attrs":{}}],"attrs":{}},{"type":"text","text":" 在 JDK 中是線程池的名稱。一個 executor 用來表示執行任務的線程池,其中最少會有一個線程,每個線程都可以用來執行同步或者異步任務。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Scheduler","attrs":{}}],"attrs":{}},{"type":"text","text":" 表示的是定時任務,定時任務的觸發,支持 JDK 中的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Timer","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Quartz Scheduler","attrs":{}}],"attrs":{}},{"type":"text","text":" 。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"2. TaskExecutor","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TaskExecutor 接口繼承了 JDK 中的 Executor。在 JDK 中,ThreadPoolExecutor 繼承了 Executor,也是一個很常用的接口。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring 對這些實現屏蔽了細節,無論是開發 Java EE 應用還是 Java SE 應用,都可以直接使用 TaskExecutor。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"TaskExecutor 的實現","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring 中已經實現了多種類型的 TaskExecutor,在絕大多數情況下,不需要自己去實現。","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"SyncTaskExecutor:用來執行非異步的任務,通常用於不需要多線程的場景,實際用的比較少,通常用來執行測試用例","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"SimpleAsyncTaskExecutor:這個實現不會重用任何的線程,每當有新任務的時候,都是重新創建一個線程","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ConcurrentTaskExecutor:這個實現是對 Executor 的適配,可以配置 Executor 的全部參數,但是一般很少使用,除非需要完全自主配置線程池","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ThreadPoolTaskExecutor:這個實現最常用,其中封裝了 ThreadPoolExecutor,如果還需要使用 Executor 的其他實現,可以使用 ConcurrentTaskExecutor","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WorkManagerTaskExecutor:這個用的就更少了,這個實現封裝了 WebLogic 的 API,以便在 WebLogic 中間件上運行 Spring 程序","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"DefaultManagedTaskExecutor:這個實現的目標是替代 WorkManagerTaskExecutor。","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"TaskExecutor 的使用","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面以最常見的 ThreadPoolTaskExecutor 爲例來演示 TaskExecutor 的使用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"創建一個待執行的任務:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class TaskDemo implements Runnable{\n\n private String message;\n\n public TaskDemo(String message) {\n this.message = message;\n }\n\n @Override\n public void run() {\n System.out.println(message);\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"再創建一個執行任務的執行器:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class SpringTaskDemo {\n private ThreadPoolTaskExecutor threadPoolTaskExecutor;\n\n public void printMessage() {\n for(int i = 0; i < 10; i++) {\n threadPoolTaskExecutor.execute(new TaskDemo(\"Hello rayjun \" + i));\n }\n }\n\n public void setThreadPoolTaskExecutor(ThreadPoolTaskExecutor threadPoolTaskExecutor) {\n this.threadPoolTaskExecutor = threadPoolTaskExecutor;\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後在容器中注入這兩個類:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\n \n \n \n\n\n \n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"再通過單元測試來執行代碼:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@ExtendWith(SpringExtension.class)\n@ContextConfiguration(\"classpath:applicationContext.xml\")\nclass SpringTaskDemoTest {\n\n @Autowired\n private ApplicationContext context;\n\n @Test\n public void test1() {\n SpringTaskDemo springTaskDemo = (SpringTaskDemo) context.getBean(\"springTaskDemo\");\n springTaskDemo.printMessage();\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"控制檯中會輸出10條消息。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3. TaskScheduler","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TaskScheduler 用來執行定時任務,與 TaskExecutor 接口只提供了一個方法不同,TaskScheduler 接口提供了很多方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這些方法都接收一個 Runnable 實例,以及表示時間或者頻率的參數。定時任務可以配置爲執行一次,也可以配置爲重複執行。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TaskSchduler 提供的方法如下:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public interface TaskScheduler {\n ScheduledFuture schedule(Runnable task, Trigger trigger);\n ScheduledFuture schedule(Runnable task, Instant startTime);\n ScheduledFuture schedule(Runnable task, Date startTime);\n ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, Duration\n period);\n ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period);\n ScheduledFuture scheduleAtFixedRate(Runnable task, Duration period);\n ScheduledFuture scheduleAtFixedRate(Runnable task, long period);\n ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTime, Duration\n delay);\n ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay);\n ScheduledFuture scheduleWithFixedDelay(Runnable task, Duration delay);\n ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay);\n }","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"TaskScheduler 實現","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TaskScheduler 有三個實現:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ThreadPoolTaskScheduler:使用的比較多,是對 JDK中的 ScheduledThreadPoolExecutor 進行包裝","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ConcurrentTaskScheduler:同樣也是對 ScheduledThreadPoolExecutor 進行包裝,但是同時也繼承了 ConcurrentTaskExecutor 來提供更好的併發度","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"DefaultManagedTaskScheduler:基於 JDNI 規範的實現,功能上與 ConcurrentTaskScheduler 相同","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"TaskScheduler 的使用","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TaskScheduler 的使用和 TaskScheduler 類似。","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\n \n\n\n \n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class SpringSchedulerTaskDemo {\n private ThreadPoolTaskScheduler threadPoolTaskScheduler;\n\n\n public void printMessage() {\n threadPoolTaskScheduler.schedule(new TaskDemo(\"Ray\"), new CronTrigger(\"0/5 * * * * ?\"));\n }\n\n\n public void setThreadPoolTaskScheduler(ThreadPoolTaskScheduler threadPoolTaskScheduler) {\n this.threadPoolTaskScheduler = threadPoolTaskScheduler;\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"SpringSchedulerTaskDemo springTaskDemo = (SpringSchedulerTaskDemo) context.getBean(\"springSchedulerTaskDemo\");\nspringTaskDemo.printMessage();","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"執行上面的代碼之後,每隔5 秒鐘就會打印一次消息。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"4. task namespace","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 Spring 中,提供了 task 的 namespace,這樣就可以少寫很多代碼。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 xml 中假如如下 namespace:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"xmlns:task=\"http://www.springframework.org/schema/task\"\nxsi:schemaLocation=\"http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd\"","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後上面創建 TaskExecutor 如下:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"創建 TaskScheduler 如下:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}}],"text":"文 / Rayjun","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文首發於微信公衆號","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/af/aff0781fde8a41d0b23e298cc79993c7.jpeg?x-oss-process=image/resize,p_80/auto-orient,1","alt":null,"title":"歡迎關注我的微信公衆號","style":[{"key":"width","value":"25%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章