Рубрики
Без рубрики

Лучшие результаты DeepCode #11: Синхронизация строк

Deep Code предлагает статический анализ программ на основе искусственного интеллекта для Java, JavaScript, TypeScript и Python…. Помеченный глубоким кодом, java, staticcodeanalysis.

Deep Code предлагает статический анализ программ на основе искусственного интеллекта для Java, JavaScript, TypeScript и Python. Как вы, возможно, знаете, Deep Code использует тысячи репозиториев с открытым исходным кодом для обучения нашего движка. Мы попросили команду разработчиков предоставить некоторые статистические данные о результатах. Что касается лучших предложений нашего движка, мы хотим представить и дать некоторую информацию в этой серии статей в блоге.

Язык: Java Дефект: Не используйте строки для синхронизации. Диагностировать: Не используйте строки для синхронизации, это может привести к нежелательным взаимоблокировкам и условиям гонки. Строки с одинаковым значением могут быть представлены в JVM как один и тот же объект.

Я нашел это в коде JDK 14. Я скопировал его части в это хранилище здесь https://github.com/CU-0xff/jdk14_parts чтобы сделать вещи управляемыми. Итак, если вы хотите продолжить, откройте это хранилище в Deep Code.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(resolved);
            }
        }
...

Фон:

Синхронизированный

Давайте сначала представим идею synchronized() . В многопоточной среде у нас возникает проблема обеспечения правильного управления доступом к переменным. Java предоставляет synchronized в качестве функций или блоков для управления параллельным доступом. Ниже приведен простой пример:

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();
    }
}

Здесь у нас есть класс Список чисел , который хранит целое число и выводит его последовательность путем зацикливания и добавления. Теперь у нас есть два потока, которые работают над одним экземпляром объекта Список чисел и все происходит так, как вы и ожидали. Мы хотели бы видеть 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 являются неизменяемыми; они не могут быть изменены. Если вы перезапишете существующий экземпляр, вы создадите новый экземпляр. Есть несколько причин, по которым разработчики языка решили сделать это, среди них то, что Строки потокобезопасны (поскольку они не изменяются, несколько потоков могут использовать их одновременно). Но есть нечто, что называется String Pool . Под капотом JVM оптимизирует использование данных. Если программа запрашивает создание строки, а строка с таким же содержимым уже существует в пуле, JVM возвращает уже существующий экземпляр вместо создания нового.

Теперь вы сталкиваетесь с более серьезной проблемой использования String в synchronized . В конечном итоге вы можете заблокировать свое приложение, потому что две строки имеют одинаковое содержимое (то есть указывают на один и тот же объект), но внутри кода они являются разными переменными. Удачи в отладке этого. Кстати, смотрите объяснение по этому поводу, предоставленное DeepCode. Я бы сказал, в точку. В нашем примере выше переменная path представляет собой строку, и ее уникальность не гарантируется.

Если вы используете synchronized() , убедитесь, что используете объект, который не является строкой. Одним из способов для нашего примера было бы использовать synchronized(this) так как он будет использовать экземпляр сложного класса.

Этот пример также показывает разницу между динамическим и статическим анализом. В динамическом случае мы должны были бы иметь случай наличия двух строк с тем же содержимым, что и в тестовом примере, или, запустив систему, перепрыгнуть через нее. В статическом случае мы проверяем, соответствует ли случай всем возможным случаям, и нажимаем на звонок.

Как вы видите из текста, DeepCode обнаружил, что эта проблема исправлена в 57 проектах, так что это не такая уж редкость. Взгляните на свой собственный код на deep code.ai .

Оригинал: “https://dev.to/deepcode/deepcode-s-top-findings-11-synchronizing-strings-1oh9”