有以下代碼:ide
public class BaseService { protected static ExecutorService executorService = Executors.newFixedThreadPool(1); } @Slf4j public class Service1 extends BaseService { public void foo() { executorService.execute(new Runnable() { @Override public void run() { try { Thread.sleep(1000 * 10); } catch (InterruptedException e) { e.printStackTrace(); } log.info("ok"); } }); } } @Slf4j public class Service2 extends BaseService { public void foo() { executorService.execute(new Runnable() { @Override public void run() { try { Thread.sleep(1000 * 10); } catch (InterruptedException e) { e.printStackTrace(); } log.info("ok"); } }); } } public class TestMain { public static void main(String[] args) { Service1 service1 = new Service1(); service1.foo(); Service2 service2=new Service2(); service2.foo(); } }
以下是程序輸出。能夠看到出現了線程排隊(間隔了10秒)。 爲何?首先,要正確理解面向對象的繼承特性,派生類繼承的是基類的非靜態成員。 也就是說,靜態的executorService是不會被繼承的; 其次,再說static,由static修飾的靜態成員,是容器啓動過程當中在初始化所在類時,就被實例化並裝載到內存裏了。在不被幹預的狀況下,其生命週期等同於整個容器服務的生命週期。咱們知道,static成員直接用「類.靜態成員」就能夠訪問。 總結來講,就像訪問一些util類的工具方法同樣,Service1裏和Service2裏訪問的至關因而BaseService.executorService,只不過由於executorService在這2個派生類裏是可見的,因此不須要顯式加」BaseService.「了。 兩個service共用的是同一個只有1個線程的線程池,那麼,天然就出現線程等待了。工具
18:54:55.465 [pool-1-thread-1] INFO stacktrace.service.Service1 - ok 18:55:05.467 [pool-1-thread-1] INFO stacktrace.service.Service2 - ok
那麼,把BaseService裏的executorService改成非靜態呢。根據面向對象的繼承特性,Service一、Service2各持有一個executorService對象,天然不存在線程排隊的狀況。再執行main方法的結果是下面這樣子的。spa
18:54:55.465 [pool-1-thread-1] INFO stacktrace.service.Service1 - ok 18:54:55.465 [pool-2-thread-1] INFO stacktrace.service.Service2 - ok
後記:線程
線程池這樣的對象必定要定義成靜態的。不然,每new一個對象,就new一個線程池對象,將會糟糕透頂。code
附記:對象
查看容器裏當前線程數,使用Thread類的靜態方法getAllStackTraces()blog
代碼: 繼承
@Slf4j public class TestMain { public static void main(String[] args) { int size = Thread.getAllStackTraces().keySet().size(); log.info("size={}", size); Iterator<Thread> iterator = Thread.getAllStackTraces().keySet().iterator(); log.info("----------"); while (iterator.hasNext()) { Thread thread = iterator.next(); StackTraceElement[] stackTraces = thread.getStackTrace(); log.info("-->{}, stackTrace.length={},state={}", thread.getName(), stackTraces.length,thread.getState()); // for (StackTraceElement element : stackTraces) { // log.info(element.toString()); // } } } }
運行結果:生命週期
21:12:36.322 [main] INFO stacktrace.service.TestMain - size=8 21:12:36.340 [main] INFO stacktrace.service.TestMain - ---------- 21:12:36.341 [main] INFO stacktrace.service.TestMain - -->main, stackTrace.length=2,瞬間狀態=RUNNABLE 21:12:36.342 [main] INFO stacktrace.service.TestMain - -->Attach Listener, stackTrace.length=0,瞬間狀態=RUNNABLE 21:12:36.343 [main] INFO stacktrace.service.TestMain - -->Signal Dispatcher, stackTrace.length=0,瞬間狀態=RUNNABLE 21:12:36.343 [main] INFO stacktrace.service.TestMain - -->pool-1-thread-1, stackTrace.length=5,瞬間狀態=TIMED_WAITING 21:12:36.344 [main] INFO stacktrace.service.TestMain - -->Reference Handler, stackTrace.length=3,瞬間狀態=WAITING 21:12:36.345 [main] INFO stacktrace.service.TestMain - -->pool-2-thread-1, stackTrace.length=5,瞬間狀態=TIMED_WAITING 21:12:36.345 [main] INFO stacktrace.service.TestMain - -->Monitor Ctrl-Break, stackTrace.length=12,瞬間狀態=RUNNABLE 21:12:36.346 [main] INFO stacktrace.service.TestMain - -->Finalizer, stackTrace.length=4,瞬間狀態=WAITING