МНОГОПОТОЧНОСТЬ Java — когда несколько потоков обращаются к методу print — почему выполнение метода while по умолчанию синхронизировано

#java

#java

Вопрос:

Сомневаетесь в многопоточности. Ниже приведена моя основная программа для доступа к файлу, я создаю 10 потоков для доступа к объекту.

 public class CallTest {
    public static void main(String[] args) throws Exception {
        Test t = new Test();
        for (int i = 0; i < 10; i  ) {
            Thread t1 = new Thread(t);

            t1.start();

        }

    }
}
  

Ниже приведена моя программа для чтения данных из файла.

 public class Test implements Runnable {
    static int i;
    public void run() {
        try {
            i  ;
            System.out.println("@@@@Count"   i);
            print();
        } catch (Exception e) {}
    }

    public void print() {
        try {
            StringBuilder bufData = new StringBuilder();
            File fileTest = new File("D:\Work\i466477");
            BufferedReader bufferedReader1 = new BufferedReader(new FileReader(
                    fileTest));
            String strRecord = new String();
            while ((strRecord = bufferedReader1.readLine()) != null) {
                bufData.append(strRecord);
                bufData.append("r");
                bufData.append("n");
            }
            bufferedReader1.close();
            System.out.println("########");
            System.out.println(bufData);

        } catch (Exception exe) {
            System.out.println(exe);
        }
    }
}
  

Здесь я мог видеть, что код в while по умолчанию синхронизирован, BufferedReader потокобезопасен или потому, что у каждого потока будет своя копия StringBuilder и BufferedReader ? Я мог видеть, что содержимое прочитано и записано правильно.

Комментарии:

1. Да, BufferedReader, скорее всего, безопасен, потому что он локальный для потока. Но почему вы думаете, что код в цикле while синхронизирован? Что вы видите, чего нет у меня?

Ответ №1:

Нет, этот код не будет синхронизирован по умолчанию. Каждый из нескольких потоков может находиться в while цикле одновременно. «Синхронизировано» — это не то же самое, что «работает без каких-либо проблем» — вы думали, что это было синхронизировано только потому, что у вас не было никаких проблем? В Java synchronized речь идет о том, чтобы разрешить только одному потоку выполнять определенные критические фрагменты кода одновременно по отношению к конкретному монитору.

Кстати, обратите внимание, что ваш доступ к i в run методе небезопасен. Вы также должны закрыть BufferedReader в finally блоке и избежать перехвата Exception . Наконец, ваше назначение new String() to strRecord для начала бессмысленно. Надеюсь, это просто ошибки из-за того, что это тестовый код, но о них стоит знать.

Комментарии:

1. Я мог видеть из выходных данных, что содержимое в buffdata синхронизировано, да, я не синхронизирован, я добавил это, чтобы протестировать для нескольких потоков метод run.

2. @Sridevi: Я думаю, вы не понимаете, что означает «синхронизированный». Пожалуйста, объясните, что вы наблюдали, не используя слово «синхронизированный», и мы сможем помочь вам понять, что происходит.

3. @Sridevi: Это совсем не буквальное использование. Код не синхронизирован — просто потоки не взаимодействуют с данными друг друга, поэтому вам не нужна синхронизация. Как я сказал в ответе, синхронизация не является синонимом потокобезопасности.

4. хорошо, потоки не совместно используют данные друг друга, потому что конструктор строк и считыватель буфера являются локальными объектами, и каждый поток будет создавать свои новые объекты правильно?

5. @Sridevi: Да, нет необходимости в синхронизации, потому что каждый поток использует свои собственные объекты … нет ничего общего (кроме файла, и с этим справляется ОС).

Ответ №2:

На самом деле System.out.println синхронизирован. Попробуйте это снова без них.

Комментарии:

1. Ваш ответ был помечен как сообщение низкого качества. Пожалуйста, обратите на это внимание.

2. @Michael — Пометка предназначена для серьезных проблем, требующих незамедлительного внимания модератора; небольшое отклонение от цели или даже ошибка — это не тот случай. (Даже совершенно неправильные ответы полезны, когда они выражают распространенное заблуждение и адекватно опровергаются в комментариях. Они могут предотвратить ту же ошибку, что и другие.) Если вы считаете, что ответ бесполезен (что разумно), вы можете проголосовать против него.

Ответ №3:

У каждого потока есть свой собственный StringBuilder, BufferedReader и FileReader (и файловый дескриптор уровня операционной системы), поэтому на этом уровне не будет никаких помех. (Ни один из этих классов не является потокобезопасным, но экземпляры ограничены потоками, так что это не имеет значения.)

Во время записи методы PrintWriter.print(...) и PrintWriter.println(...) синхронизированы, и это объясняет, почему вы не видите выходных данных от отдельных вызовов println, смешанных вместе. (PrintWriter потокобезопасен… и должно быть.)

Примечание: если вы изменили свой код, включив номер потока в каждую напечатанную строку, вы можете иногда видеть, что выходные данные отображаются в неожиданном порядке. Отдельные вызовы потокобезопасного метода для одного и того же объекта (PrintWriter) не обязательно выполняются в порядке «первым пришел, первым обслужен».


Код, который обновляет статическую переменную, i не является потокобезопасным и может время от времени выдавать неожиданные (неправильные) результаты… в зависимости от того, какое оборудование / JVM вы используете. Вам следует либо выполнить обновление в synchronized static методе, либо заменить i на AtomicInteger .

Комментарии:

1. хорошо, спасибо за объяснение, мои данные не являются общими, потому что у каждого потока есть свои собственные локальные средства построения строк и считыватель буфера, а оператор вывода по умолчанию потокобезопасен, спасибо за знания

Ответ №4:

Локальные переменные ограничены потоком. Но неатомные операции (например, i ) со статической переменной i не являются потокобезопасными.

Ответ №5:

bufferreader и stringbuilder не являются общими для потоков, поэтому их использование потокобезопасно.

StringBuffer в определенной степени потокобезопасен, поскольку все его методы синхронизированы. BufferedReader не является потокобезопасным.

Комментарии:

1. StringBuilder не является потокобезопасным. Вы думаете о StringBuffer .