java—用非静态成员变量初始化静态变量一次

eit6fx6z  于 2021-07-09  发布在  Java
关注(0)|答案(2)|浏览(472)

我想计算使用https://micrometer.io/docs/concepts#_gauges. 从文档中,最好在集合上创建一个量规(我没有),或者使用类似atomicinteger的东西。
但是,我需要一个atomicinteger的静态示例,必须使用成员变量meterregistry对其进行初始化。最好的做法是什么?我不想使用标准的单例模式,因为这意味着我必须始终调用getinstance()来获取atomicinteger示例,而且每次都必须同步。
有没有比我现在做的更好的方法?

public class RunInAThread implements Runnable {

    private static AtomicInteger GAUGE = null;

    public RunInAThread(final MeterRegistry registry) {
        synchronized(this) {
            if(GAUGE==null) {
                GAUGE = registry.gauge("some_name", Collections.emptySet(), new AtomicInteger());
            }
        }     
    }

    @Override
    public void run() {
        GAUGE.incrementAndGet()
        doSomething();
        GAUGE.decrementAndGet()
    }

    private void doSomething() {   
       // processing like a boss in a while loop   
    }

}
kiz8lqtg

kiz8lqtg1#

你当前的同步呼叫没有按你的想法进行。在“this”上同步不会阻止runinathread的两个示例同时被示例化,并且两个示例都会检测gauge为null并设置它。
从您的代码示例中,不清楚为什么gauge必须是静态的。它来自注册表,不清楚是否有任何保证两个不同的注册表对象将返回相同的atomicinteger。现在,如果meterregistry是一个单例,那么有一个选项是使用单例。比如:

private static final AtomicInteger GAUGE = MeterRegistry.getInstances().gauge(...)

编辑代码:

MeterRegistry reg1 = new MeterRegistry(...);
RunInAThread thread1 = RunInAThread(reg1)

MeterRegistry reg2 = new MeterRegistry(...);
RunInAThread thread2 = RunInAThread(reg1)

在这种情况下,是否真的打算在两种情况下使用相同的量规?
考虑到这一点,以前使注册表成为单例的解决方案仍然有效。或者,传入原子整数并将其视为成员变量。这将更加明确,不容易出现意想不到的行为。

MeterRegistery reg = new MeterRegistry(...);
AtomicInteger gauge = reg.gauge(...);
RunInAThread thread1 = RunInAThread(gauge);
RunInAThread thread2 = RunInAThread(gauge);

我认为关键是你正在努力解决这个问题,而这个问题的水平低于你应该达到的水平。

yrdbyhpb

yrdbyhpb2#

synchronized(this) 在构造函数中是完全无用的。这意味着:避免将大括号中包含的代码与锁定同一对象的任何其他线程同时运行。你锁定的那个物体呢?从定义上讲,不是任何其他线程都可能拥有的—您只是被创建的*。
听起来像这样 MeterRegistry 概念本身就是一个单子。如果你能在其他静态块中初始化那个量表一次,也许可以进行调查。但是,如果这看起来很困难或不可能,那么如果您真的想从中挤出性能,您可以使用双重锁定;不过,我怀疑这有什么关系。同步速度很快。不管怎样,理论上这应该更快:

public class RunInAThread implements Runnable {
    private static final Object GAUGE_LOCK = new Object();
    private static AtomicInteger GAUGE = null;

    public RunInAThread(final MeterRegistry registry) {
        if (GAUGE == null) {
            synchronized (GAUGE_LOCK) {
                if (GAUGE == null) GAUGE = registry.gauge(...);
            }
        }
    }
}

这可以做几件事:
这把锁实际上很有用。虚拟机中只有一个gauge\u lock对象,所以如果我们点击了synchronized,它就会工作。同步还建立在前面,从而保证vm将确保我们对gauge变量的视图得到更新;因此,没有必要使 Jmeter 挥发。
如果幸运的话,由于任何原因,gauge变量已经更新,那么我们就永远不会同步。
由于第二次的nullcheck,gauge仍然不可能是除[a]null或[b]一次的返回值以外的任何东西,我们称之为registry。
这称为“双重检查锁定”。两个空检查是至关重要的。

  • )你可以让 synchronized(this) 在构造函数中实际上有一个效果,但前提是您要么在自己的构造函数中触发线程,要么让 this 从构造器中逃逸。这两件事都是很荒谬的坏事,我觉得假设你不会做那么愚蠢的事是有道理的。在这一点上,我们可以简化为: synchronized(this) 在构造函数中是无用的。

相关问题