当static遇到类继承。。也许你会懵。。
继承 遇到 static 也许
2023-09-14 09:06:26 时间
有如下代码:
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
相关文章
- C语言多态与继承
- [Android Pro] Android开发实践:为什么要继承onMeasure()
- 面试-封装、继承、多态
- 【PHP面向对象(OOP)编程入门教程】11.类的继承
- JAVA 【引用类型】和【对象类型】在【继承】中的异同
- .Net 配置文件--继承ConfigurationSection实现自定义处理类处理自定义配置节点
- 修改继承窗体中的控件属性
- C# 继承
- TypeScript里的interface扩展,多继承以及对应的JavaScript代码
- C# 继承
- Atitit利用反射获取子类 集合 以及继承树
- 【项目实战】SpringMVC配置全局属性,是实现WebMvcConfigurer接口,还是直接继承WebMvcConfigurationSupport类?
- C++多重继承与void*指针转换问题
- C++虚继承下的内存模型(一)
- Javascript有几种继承方式?这下你该知道了
- Go Struct 继承 匿名字段和内嵌结构体