一起 Static 和 Synchronized 引发的血案

这两天在定位一个网上问题的时候发现一个很诡异的现象,系统夜间的汇总任务跑了很长一段时间才能结束,而且日志显示这些汇总任务的每个子任务都很快就结束了,但整体任务还是耗费了很长一段时间才结束。

sub-job 1 done in 3ssub-job 2 done in 3ssub-job 3 done in 2ssub-job 4 done in 5ssub-job 5 done in 6ssub-job 6 done in 8ssub-job 7 done in 9s...whole process is down in 3235s

其实整体业务流程很简单,大致的流程就是系统创建了很多汇总任务,把它们丢到线程池中去执行。这些任务在执行的过程中,为了提高效率,会创建一些子任务并并发的运行它们,当子任务运行结束后,父任务就会结束,所以出现这种现象是非常不科学的。我的第一感觉就是是不是任务间存在不合理的锁竞争导致线程相互等待?仔细检查代码,果然发现了问题,在汇总任务的父类中有这样一个方法:

private static synchronized format(DateTime dt){ return "P" + dt.toString("yyyyMMHHmmss");}

这个方法是汇总任务根据时间生成目标汇总时间周期用的,之所以会封装成一个方法,估计是为了代码复用考虑。封装本身并没有错,但是要命的是,开发人员将方法声明为static synchronized,让我们先回忆一下这个两个关键字的作用:

  • synchronized synchronized 关键字放在方法声明上时,表示该方法为Synchronized Methods,即同步方法,在The Java™ Tutorials中对同步方法有以下描述: First, it is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object. Second, when a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the state of the object are visible to all threads. 简单来说就是当一个方法声明为同步方法的时候,不可能出现多个线程同时调用同一个对象(注意是同一个对象,这点很重要)上的该方法,只有当一个线程调用结束,其他线程才有可能获取锁并执行该方法。
  • static 在Java中static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,当然也可以修饰代码块。被static修饰的成员变量和成员方法是独立于该类的,它不依赖于某个特定的实例变量,也就是说它被该类的所有实例共享。所有实例的引用都指向同一个地方,任何一个实例对其的修改都会导致其他实例的变化。

那么synchronized加上static会出现什么效果?按照上面的分析static是整个类共享的,不仅仅是一个对象,那么static synchronized修饰的变量、方法或者代码段就是在类的粒度上进行同步,而不是仅仅是在对象粒度上。对于这个问题,Java machine language specification中也有描述:

For a class (static) method, the monitor associated with the Class object for the method’s class is used.

For an instance method, the monitor associated with this (the object for which the method was invoked) is used.

所以在我们的业务代码中,如果在父类中声明了一个static synchronized的方法,就意味着每个继承它的子类及其对象在调用这个方法时都会争夺这个锁,那么造成任务执行效率低下也就是必然的了。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏swag code

Java中的private、protected、public和default的区别(详解)

(1)对于public修饰符,它具有最大的访问权限,可以访问任何一个在CLASSPATH下的类、接口、异常等。它往往用于对外的情况,也就是对象或类对外的一种接口...

803
来自专栏编程

java知识点归纳

java新手知识点归纳-java基础部分 ? 一. Java的运行(基础必备) 这条可能出看很简单,java程序的运行谁不会呢?不过很多时候, 我们只是单纯通过...

2226
来自专栏Java职业技术分享

Java技术——你真的了解String类的intern()方法吗

是不是感觉莫名其妙,新定义的str2好像和str1没有半毛钱的关系,怎么会影响到有关str1的输出结果呢?其实这都是intern()方法搞的鬼!看完这篇文章,你...

1470
来自专栏武军超python专栏

2018-7-17 python中函数的讲解

定义函数的基本语法:【定义/声明函数、调用函数】 define 定义 param 参数

1114
来自专栏步履前行

深入理解JVM--(1)运行时的数据区域划分--java虚拟机栈

  之前我们了解了程序计数器,接下来了解第二个线程私有的数据区域--虚拟机栈。 虚拟机栈是线程私有的,每创建一个线程,虚拟机就会为这个线程创建一个虚拟机栈,虚...

3175
来自专栏黑泽君的专栏

java注解用法详解——@SuppressWarnings

  在java编译过程中会出现很多警告,有很多是安全的,但是每次编译有很多警告影响我们对error的过滤和修改,我们可以在代码中加上 @SuppressWarn...

1.1K3
来自专栏全沾开发(huā)

拿Proxy可以做哪些有意思的事儿

2528
来自专栏蛋未明的专栏

PHP分割两个数组的相同元素和不同元素的两种方法

2154
来自专栏Java帮帮-微信公众号-技术文章全总结

【大牛经验】探讨Java的异常与错误处理

探讨Java的异常与错误处理 ENTER TITLE ? Java中的异常处理机制已经比较成熟,我们的Java程序到处充满了异常的可能,如果对这些异常不做预先的...

3766
来自专栏CDA数据分析师

工具 | 学习总结:当我学完Python我学了些什么

本文是本人学完Python后的一遍回顾,加深理解顺便留作手册以备查阅。 学习Python的这几天来,觉得Python还是比较简单,容易上手的,就基本语法而言,...

21210

扫码关注云+社区

领取腾讯云代金券