-1

下面是我的课程,用于CountDownLatch确保在这些地图上发生写入时第一次不会在主要、次要和三级地图上发生读取。

public class ClientData {

    public static class Mappings {
        public final Map<String, Map<Integer, String>> primary;
        public final Map<String, Map<Integer, String>> secondary;
        public final Map<String, Map<Integer, String>> tertiary;

        public Mappings(
            Map<String, Map<Integer, String>> primary,
            Map<String, Map<Integer, String>> secondary,
            Map<String, Map<Integer, String>> tertiary
        ) {
            this.primary = primary;
            this.secondary = secondary;
            this.tertiary = tertiary;
        }
    }

    private static final AtomicReference<Mappings> mappings = new AtomicReference<>();
    private static final CountDownLatch hasBeenInitialized = new CountDownLatch(1);

    public static Mappings getMappings() {
        try {
            hasBeenInitialized.await();
            return mappings.get();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new IllegalStateException(e);
        }
    }

    public static void setMappings(
        Map<String, Map<Integer, String>> primary,
        Map<String, Map<Integer, String>> secondary,
        Map<String, Map<Integer, String>> tertiary
    ) {
        setMappings(new Mappings(primary, secondary, tertiary));
    }

    public static void setMappings(Mappings newMappings) {
        mappings.set(newMappings);
        hasBeenInitialized.countDown();
    }
}

下面是我的后台线程类,它只负责设置所有三个映射(在下面查找 parseResponse 方法)。它每 10 分钟运行一次。

public class TempBackgroundThread {

    // parse the response and store it in a variable
    private void parseResponse(String response) {
        //...       
        Map<String, Map<Integer, String>> primaryTables = null;
        Map<String, Map<Integer, String>> secondaryTables = null;
        Map<String, Map<Integer, String>> tertiaryTables = null;

        //...

        // store the three map data in ClientData class variables if anything has changed  
        // which can be used by other threads, this will be updated once every four or five months
        if(changed) {
            ClientData.setMappings(primaryTables, secondaryTables, tertiaryTables);
        }
    }
}

问题陈述:

如果我对我的映射对象以及主要、次要和三次映射进行各种空值或完整性检查,性能会大大降低(不知道为什么)。但是,如果我不做任何健全性或空值检查,性能就会非常好。谁能解释我出了什么问题以及为什么会发生?

下面是一个例子 -

我正在使用ClientData类来获取主线程中的所有映射。正如您在下面看到的,我正在做各种健全性检查以确保、 和mappingsmappings.primary为空。如果它们为空,则记录错误并返回mappings.secondarymappings.tertiary

class Task implements Callable<String> {

    public Task() {
    }

    public String call() throws Exception {

        int compId = 100;
        String localPath = "hello";
        String remotePath = "world";

        Mappings mappings = ClientData.getMappings(); 

        if (MyUtilityClass.isEmpty(mappings)
                || (MyUtilityClass.isEmpty(mappings.primary) && MyUtilityClass
                        .isEmpty(mappings.secondary))
                || MyUtilityClass.isEmpty(mappings.tertiary)) {

            // log error and return
        }

        // otherwise extract values from them
        String localPAddress = null;
        String remotePAddress = null;
        if (MyUtilityClass.isNotEmpty(mappings.primary)) {
            String localPId = mappings.primary.get(localPath).get(compId);
            localPAddress = mappings.tertiary.get(localPath).get(
                    Integer.parseInt(localPId));
            String remotePId = mappings.primary.get(remotePath).get(compId);
            remotePAddress = mappings.tertiary.get(remotePath).get(
                    Integer.parseInt(remotePId));
        }

        String localSAddress = null;
        String remoteSAddress = null;
        if (MyUtilityClass.isNotEmpty(mappings.secondary)) {
            String localSId = mappings.secondary.get(localPath).get(compId);
            localSAddress = mappings.tertiary.get(localPath).get(
                    Integer.parseInt(localSId));
            String remoteSId = mappings.secondary.get(remotePath).get(compId);
            remoteSAddress = mappings.tertiary.get(remotePath).get(
                    Integer.parseInt(remoteSId));
        }

        // now use - localPAddress, remotePAddress, localSAddress and remoteSAddress
    }
}

通过对一级、二级和三级映射的上述健全性和空值检查,应用程序的整体性能(第 95 个百分位)为 4 毫秒。

但是,如果我在没有对主要、次要和三次映射进行任何健全性检查或空值检查的情况下这样做,我会得到 0.87 毫秒的整体性能(第 95 个百分位)。

class Task implements Callable<String> {

    public Task() {
    }

    public String call() throws Exception {

        int compId = 100;
        String localPath = "hello";
        String remotePath = "world";

        Mappings mappings = ClientData.getMappings(); 

        String localPId = mappings.primary.get(localPath).get(compId);
        String localPAddress = mappings.tertiary.get(localPath).get(Integer.parseInt(localPId));
        String remotePId = mappings.primary.get(remotePath).get(compId);
        String remotePAddress = mappings.tertiary.get(remotePath).get(Integer.parseInt(remotePId));
        String localSId = mappings.secondary.get(localPath).get(compId);
        String localSAddress = mappings.tertiary.get(localPath).get(Integer.parseInt(localSId));
        String remoteSId = mappings.secondary.get(remotePath).get(compId);
        String remoteSAddress = mappings.tertiary.get(remotePath).get(Integer.parseInt(remoteSId));

        // now use - localPAddress, remotePAddress, localSAddress and remoteSAddress
    }
}

下面是我的 isEmpty 和 isNotEmpty 方法 -

public static boolean isNotEmpty(Object obj) {
    return !isEmpty(obj);
}

public static boolean isEmpty(Object obj) {
    if (obj == null)
        return true;
    if (obj instanceof Collection)
        return ((Collection<?>) obj).size() == 0;

    final String s = String.valueOf(obj).trim();

    return s.length() == 0 || s.equalsIgnoreCase("null");
}
4

2 回答 2

1

看看你的代码多久到达这一点。对于一些复杂的对象及其繁重的 #toString() 方法,这可能会很昂贵:

final String s = String.valueOf(obj).trim();

它还会在您的测试计数时创建可能导致垃圾收集的临时垃圾。

于 2014-07-05T22:01:38.453 回答
0

您的健全性检查确保除非一切都完美,否则不会执行任何代码。如果任何检查失败,您记录然后返回。仅当健全性检查成功时,您才能继续。

相反,考虑盲目地进行,假设一切都很好。但是用try-catch围绕每个关键部分。然后在捕获中检查特定错误,只是为了获得准确的异常消息。

例如,永远不要这样做:

public void getStringLength(String string_thatShouldNeverBeNull)  {
   Objects.requireNonNull(string_thatShouldNeverBeNull, "string_thatShouldNeverBeNull");
   return  string_thatShouldNeverBeNull.length();
}

当您可以这样做时:

public void getStringLength(String string_thatShouldNeverBeNull)  {
   try  {
      return  string_thatShouldNeverBeNull.length();
   }  catch(NullPointerException npx)  {
      throw  new NullPointerException("string_thatShouldNeverBeNull");
   }      
}

原因是这两个函数的输出/错误响应总是相同的,无论参数是否null。那么,为什么要在大多数时间可能有效的情况下进行额外检查呢?这很浪费。

在某些情况下这是不可能的。一个例子:

public void setTheString(String string_thatShouldNeverBeNull)  {
   Objects.requireNonNull(string_thatShouldNeverBeNull, "string_thatShouldNeverBeNull");
   str = string_thatShouldNeverBeNull;
}

永远没有机会抛出错误,因此必须进行检查。

于 2014-07-05T22:17:38.833 回答