當static遇到類繼承。。也許你會懵。。

有以下代碼: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
相關文章
相關標籤/搜索