当static遇到类继承。。也许你会懵。。

有如下代码:

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改为非静态呢。根据面向对象的继承特性,Service1、Service2各持有一个executorService对象,自然不存在线程排队的情况。再执行main方法的结果是下面这样子的。

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一个线程池对象,将会糟糕透顶。


附记:

查看容器里当前线程数,使用Thread类的静态方法getAllStackTraces()

 代码: 

@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
原文地址:https://www.cnblogs.com/buguge/p/14287346.html