DeepCode 最重要的发现:同步字符串

DeepCode为Java、JavaScript和TypeScript以及Python提供了基于AI的静态程序分析。如您所知,DeepCode使用数千个开源repos来训练我们的引擎。我们要求引擎团队提供一些调查结果的统计数据。在我们的引擎的顶部建议中,我们想在本系列的博客文章中介绍和给出一些背景知识。

语言: Java
缺陷:不要使用字符串进行同步。
诊断:不要使用字符串进行同步,这可能导致不必要的死锁和竞争条件。具有相同值的字符串在JVM中可能表示为同一对象。

我在JDK 14代码中找到了这个。我将部分内容复制到了此仓库中,网址https://github.com/CU-0xff/jdk14_parts,以使事情易于管理。因此,如果您想继续,请在DeepCode.ai中打开该存储库这是一个非常令人讨厌的问题,因为它迫使您了解JVM的一些实现细节,以理解这个问题。以下是感兴趣的代码:


class WindowsPath implements Path {
...
    // normalized path
    private final String path;
...
        // cache the resolved path (except drive relative paths as the working
        // directory on removal media devices can change during the lifetime
        // of the VM)
        if (type != WindowsPathType.DRIVE_RELATIVE) {
            synchronized (path) {
                pathForWin32Calls = new WeakReference<String>(resolved);
            }
        }
...

背景:

已同步

让我们首先介绍synchronized()的概念。在多线程环境中,我们需要确保正确地管理对变量的访问。Java提供同步的函数或块来管理并发访问。下面是一个简单的例子:


class ListOfNumbers {
    int number;

    void printListOfNumbers(final int n) {
        this.number = 0;
        for (int i = 1; i <= 10; i++) {
            try {
                // Simulate some substantial work
                Thread.sleep(400);
            } catch (final Exception e) {
                System.out.println(e);
            }
            this.number += n;
            System.out.print(String.format("%d ",this.number));
        }
    }
}

class MyThread extends Thread {
    ListOfNumbers l;
    int flag;

    MyThread(final ListOfNumbers l, int flag) {
        this.l = l;
        this.flag = flag;
    }

    public void run() {
        l.printListOfNumbers(flag);
    }
}


class TestSynchronization1 {
    public static void main(final String args[]) {
        final ListOfNumbers obj = new ListOfNumbers();// only one object
        final MyThread t1 = new MyThread(obj, 1);
        final MyThread t2 = new MyThread(obj, 7);
        t1.start();
        t2.start();
    }
}

在这里,我们有一个class ListOfNumbers,它存储一个整数并通过循环和添加来打印一系列整数。现在,我们有两个线程正在处理的一个对象实例,ListOfNumbers并且它按您期望的那样发生。我们想在控制台上看到1 2 3 4 5 6 7 8 9 10 7 14 21 28 35 42 49 56 63 70,但是我们得到8 8 9 16 24 24 25 32 39 39 46 47 55 55 62 62 69 69 76 77,然后1 1 9 9 17 10 24 25 32 32 40 40 48 48 55 56 57 57 65 65,然后...

这告诉我们两件事:(1)显然,两个线程指向同一对象时彼此会覆盖。(2)结果是不可预测的,因为它取决于线程调用方式的顺序。

通过简单的更改,我们可以对问题进行排序:


class MyThread extends Thread {
...
    public void run() {
        synchronized(l) {
            l.printListOfNumbers(flag);
        }
    }
...

这样一来,线程会在通过调用方法更改实例状态之前要求对实例进行控制。 到现在为止还挺好。

这样一来,线程在通过调用方法更改其状态之前要求对实例进行控制。到目前为止,一切都很好。

为什么不使用synchronized()字符串?

在Java中Strings是不可变的;它们无法更改。如果覆盖现有实例,则会生成一个新实例。语言设计者决定这样做的原因有很多,其中有一个Strings是线程安全的(因为它们不会更改,因此多个线程可以同时使用它们)。但是有些东西叫做String Pool。在后台,JVM优化了数据使用。如果程序要求创建一个字符串,并且池中已经存在一个具有相同内容的字符串,则JVM将返回现有实例,而不是创建一个新实例。

现在,更大的问题是如何在synchronized中使用字符串。您可能会锁定应用程序,因为两个字符串具有相同的内容(也就是指向相同的对象),但是在代码中,它们是不同的变量。祝您调试成功。顺便说一下,请参阅deepcode在这方面提供的解释。我会说,完全正确。在上面的例子中,path变量是一个字符串,它不能保证是唯一的。

如果使用synchronized(),请确保使用的对象不是字符串。我们的示例的一种方法是使用synchronized(this),因为它将使用一个复杂类的实例。

此示例还显示了动态和静态分析之间的区别。在动态情况下,我们将不得不拥有两个内容与测试用例具有相同内容的字符串,或者通过运行系统来取代它。在静态案例中,我们测试案例是否在所有可能的案例之内,然后按铃。

从文本中可以看到,DeepCode在57个项目中发现了此问题,因此并不少见。在deepcode.ai上查看您自己的代码。

原文链接: https://dev.to//deepcode/deepcode-s-top-findings-11-synchronizing-strings-1oh9

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值