Какова самая частая проблема параллелизма, с которой Вы встретились в Java? [закрытый]

190
задан 5 revs, 3 users 75% 5 August 2012 в 12:49
поделиться

47 ответов

Наиболее распространенная проблема параллелизма, которую я видел, не понимает, что поле, записанное одним потоком, не гарантировано , чтобы быть замеченным различным потоком. Распространенное приложение этого:

class MyThread extends Thread {
  private boolean stop = false;

  public void run() {
    while(!stop) {
      doSomeWork();
    }
  }

  public void setStop() {
    this.stop = true;
  }
}

, пока остановка не энергозависима или setStop, и run не , синхронизировался , это, как гарантируют, не будет работать. Эта ошибка является особенно дьявольской как в 99,999%, она не будет иметь значения на практике, поскольку поток читателя будет в конечном счете видеть изменение - но мы не знаем, как скоро он видел его.

123
ответ дан 2 revs, 2 users 97% 23 November 2019 в 05:35
поделиться

Использование глобального объекта, такого как статическая переменная для блокировки.

Это приводит к очень плохой производительности из-за конкуренции.

3
ответ дан kohlerm 23 November 2019 в 05:35
поделиться

Honesly? До появления java.util.concurrent, наиболее распространенная проблема, с которой я обычно сталкивался, была тем, что я называю "перегрузкой потока": Приложения, которые используют потоки для параллелизма, но порождают слишком многих из них и заканчивают тем, что перегрузились.

3
ответ дан Brian Clapper 23 November 2019 в 05:35
поделиться

Не понимание, что this во внутреннем классе не this из внешнего класса. Обычно в анонимном внутреннем классе, который реализует Runnable. Корневая проблема состоит в том, что, потому что синхронизация является частью всего Object, с там не является эффективно никакой статической проверкой типа. Я видел это, по крайней мере, дважды в Usenet, и это также появляется в Brian Goetz'z Java Concurrency на практике.

закрытия BGGA не страдают от этого, поскольку нет никакого this для закрытия (this ссылки внешний класс). Если Вы используете не - this объекты как блокировки тогда, это обходит эту проблему и других.

5
ответ дан Tom Hawtin - tackline 23 November 2019 в 05:35
поделиться

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

6
ответ дан Brendan Cashman 23 November 2019 в 05:35
поделиться

Другая общая проблема 'параллелизма' должна использовать синхронизируемый код, когда это не необходимо вообще. Например, я все еще вижу, что программисты используют StringBuffer или даже java.util.Vector (как локальные переменные метода).

7
ответ дан Kutzi 23 November 2019 в 05:35
поделиться

Пока я не посещал урок с Brian Goetz, я не понял, что несинхронизируемый getter из частного поля, видоизмененного через синхронизируемый setter, никогда , гарантировал, что возвратил обновленное значение. Только, когда переменная защищена синхронизируемым блоком на и чтения И записи будут Вы получать гарантию последнего значения переменной.

public class SomeClass{
    private Integer thing = 1;

    public synchronized void setThing(Integer thing)
        this.thing = thing;
    }

    /**
     * This may return 1 forever and ever no matter what is set
     * because the read is not synched
     */
    public Integer getThing(){
        return thing;  
    }
}
21
ответ дан 2 revs, 2 users 97% 23 November 2019 в 05:35
поделиться

Типичная проблема использует классы как Календарь и SimpleDateFormat от нескольких потоков (часто путем кэширования их в статической переменной) без синхронизации. Эти классы не ориентированы на многопотоковое исполнение, таким образом, многопоточный доступ в конечном счете вызовет странные проблемы с непоследовательным состоянием.

49
ответ дан Alex Miller 23 November 2019 в 05:35
поделиться

Перепроверяемая Блокировка. В общем и целом.

парадигма, которая я начал изучать проблемы того, когда я работал в BEA, то, что люди проверят одиночный элемент следующим образом:

public Class MySingleton {
  private static MySingleton s_instance;
  public static MySingleton getInstance() {
    if(s_instance == null) {
      synchronized(MySingleton.class) { s_instance = new MySingleton(); }
    }
    return s_instance;
  }
}

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

  public static MySingleton getInstance() {
    if(s_instance == null) {
      synchronized(MySingleton.class) {
        if(s_instance == null) s_instance = new MySingleton();
      }
    }
    return s_instance;
  }

, Который не работает также, потому что Модель памяти Java не поддерживает его. Необходимо объявить, что s_instance как энергозависимый заставляет его работать, и даже тогда это только работает над Java 5.

Люди, которые не знакомы с запутанностью Модели памяти Java, смешивают это все время .

46
ответ дан Kirk Wylie 23 November 2019 в 05:35
поделиться

Не правильно синхронизация на объектах, возвращенных Collections.synchronizedXXX(), особенно во время повторения или нескольких операций:

Map<String, String> map = Collections.synchronizedMap(new HashMap<String, String>());

...

if(!map.containsKey("foo"))
    map.put("foo", "bar");

Это неправильно . Несмотря на единственные операции, являющиеся synchronized, состояние карты между вызовом contains и put может быть изменено другим потоком. Это должно быть:

synchronized(map) {
    if(!map.containsKey("foo"))
        map.put("foo", "bar");
}

Или с ConcurrentMap реализация:

map.putIfAbsent("foo", "bar");
45
ответ дан 4 revs, 4 users 81% 23 November 2019 в 05:35
поделиться

Хотя, вероятно, не точно, что Вы просите, самая частая связанная с параллелизмом проблема, с которой я встретился (вероятно, потому что она подходит в нормальном однопоточном коде)

java.util.ConcurrentModificationException

вызваны вещами как:

List<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c"));
for (String string : list) { list.remove(string); }
36
ответ дан Fabian Steeg 23 November 2019 в 05:35
поделиться

Наиболее распространенная ошибка, которую мы видим, где я работаю, является программистами, выполняют долгие операции, как вызовы сервера, на EDT, запирая GUI в течение нескольких секунд и делая приложение безразличным.

28
ответ дан Eric Burke 23 November 2019 в 05:35
поделиться

Упущение ожидать () (или Condition.await ()) в цикле, проверяя, что условие ожидания на самом деле верно. Без этого Вы сталкиваетесь с ошибками от побочного ожидания () пробуждения. Каноническое использование должно быть:

 synchronized (obj) {
     while (<condition does not hold>) {
         obj.wait();
     }
     // do stuff based on condition being true
 }
27
ответ дан Alex Miller 23 November 2019 в 05:35
поделиться

Может быть легко думать, синхронизировался, наборы предоставляют Вам больше защиты, чем они на самом деле делают и забывают держать блокировку между вызовами. Я видел эту ошибку несколько раз:

 List<String> l = Collections.synchronizedList(new ArrayList<String>());
 String[] s = l.toArray(new String[l.size()]);

, Например, во второй строке выше, toArray() и size() методы и ориентированы на многопотоковое исполнение самостоятельно, но эти size() оценен отдельно от эти toArray(), и блокировка в Списке не сохранена между этими двумя вызовами.

при выполнении этого кода с другим потоком одновременно объекты удаления из списка, рано или поздно Вы закончите с новым String[], возвратился, который больше, чем необходимый для содержания всех элементов в списке и имеет нулевые значения в хвосте. Легко думать что, потому что эти два вызова метода Списка происходят в одной строке кода, это - так или иначе атомарная операция, но это не.

29
ответ дан 3 revs, 2 users 83%Nick 23 November 2019 в 05:35
поделиться

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

25
ответ дан Eric Burke 23 November 2019 в 05:35
поделиться

Используя локальный "новый Объект ()" как взаимное исключение.

synchronized (new Object())
{
    System.out.println("sdfs");
}

Это бесполезно.

7
ответ дан Ludwig Wensauer 23 November 2019 в 05:35
поделиться

Думая Вы пишете однопоточный код, но используете изменяемые помехи (включая одиночные элементы). Очевидно, они будут совместно использованы потоками. Это происходит удивительно часто.

14
ответ дан Tom Hawtin - tackline 23 November 2019 в 05:35
поделиться

Произвольные вызовы метода не должны быть сделаны из синхронизируемых блоков.

Dave Ray, тронутый этого в его первом ответе, и на самом деле, я также встретился с мертвой блокировкой, также имеющей отношение к вызову методов на слушателях из синхронизированного метода. Я думаю, что более общий урок - то, что вызовы метода не должны быть превращены "в дикую местность" из синхронизируемого блока - Вы понятия не имеете, будет ли вызов продолжителен, результат в мертвой блокировке, или что бы то ни было.

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

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

14
ответ дан Scott Bale 23 November 2019 в 05:35
поделиться

Новая Связанная с параллелизмом ошибка, с которой я столкнулся, была объектом, который в его конструкторе создал ExecutorService, но когда на объект больше не ссылались, это, никогда не имело не завершают работу ExecutorService. Таким образом, в течение недель, тысячи из пропущенных потоков, в конечном счете заставляя систему отказать. (Технически, это не отказало, но это действительно прекращало функционировать правильно, продолжая работать.)

Технически, я предполагаю, что это не проблема параллелизма, но это - проблема, имеющая отношение к использованию java.util.concurrency библиотек.

12
ответ дан Eddie 23 November 2019 в 05:35
поделиться

Я встретился с проблемой параллелизма с Сервлетами, когда существуют изменяемые поля, которые будут setted каждым запросом. Но существует только один экземпляр сервлета для всего запроса, таким образом, это работало отлично в среде отдельного пользователя, но когда больше чем один пользователь запросил сервлет произошли, непредсказуемые результаты.

public class MyServlet implements Servlet{
    private Object something;

    public void service(ServletRequest request, ServletResponse response)
        throws ServletException, IOException{
        this.something = request.getAttribute("something");
        doSomething();
    }

    private void doSomething(){
        this.something ...
    }
}
10
ответ дан Ludwig Wensauer 23 November 2019 в 05:35
поделиться

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

РЕДАКТИРОВАНИЕ: Перемещенная вторая часть для разделения ответа.

9
ответ дан 2 revs 23 November 2019 в 05:35
поделиться

Несбалансированная синхронизация, особенно против Карт, кажется, довольно типичная проблема. Многие люди полагают, что синхронизация на помещает в Карту (не ConcurrentMap, но скажите HashMap), и не синхронизирующийся на добирается, достаточно. Это однако может привести к бесконечному циклу во время рехеширования.

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

10
ответ дан 2 revs 23 November 2019 в 05:35
поделиться

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

[еще 111], люди должны использовать аннотации параллелизма (например, @ThreadSafe, @GuardedBy и т.д.) описанный в книге Goetz.

10
ответ дан Neil Bartlett 23 November 2019 в 05:35
поделиться

Запуск потока в конструкторе из класса проблематичен. Если класс расширяется, поток может быть запущен , прежде чем конструктор подкласса будет выполнен.

9
ответ дан 2 revs, 2 users 67% 23 November 2019 в 05:35
поделиться

Изменяемые классы в структурах совместно используемых данных

Thread1:
    Person p = new Person("John");
    sharedMap.put("Key", p);
    assert(p.getName().equals("John");  // sometimes passes, sometimes fails

Thread2:
    Person p = sharedMap.get("Key");
    p.setName("Alfonso");

, Когда это происходит, код, намного более сложны что этот упрощенный пример. Тиражирование, нахождение и исправление ошибки трудны. Возможно, этого можно было избежать, если мы могли бы отметить определенные классы как неизменные и определенные структуры данных как только содержание неизменных объектов.

8
ответ дан Steve McLeod 23 November 2019 в 05:35
поделиться

Я верю в будущее, которым основная проблема с Java будет (отсутствие) гарантии видимости конструкторов. Например, если Вы создаете следующий класс

class MyClass {
    public int a = 1;
}

и затем просто читаете свойство MyClass от другого потока, MyClass.a мог быть или 0 или 1, в зависимости от реализации и настроения JavaVM. Сегодня возможности для существа 1 очень высоки. Но на будущих машинах NUMA это может отличаться. Многие люди не знают об этом и полагают, что они не должны заботиться о многопоточности во время фазы инициализации.

8
ответ дан 3 revs, 2 users 93% 23 November 2019 в 05:35
поделиться

Синхронизация на строковом литерале или постоянный определенный строковым литералом является (потенциально) проблемой, поскольку строковый литерал интернируется и будет совместно использован кем-либо еще в JVM с помощью того же строкового литерала. Я знаю, что эта проблема подошла в серверах приложений и других "контейнерных" сценариях.

Пример:

private static final String SOMETHING = "foo";

synchronized(SOMETHING) {
   //
}

В этом случае, любой использующий строку "нечто" для соединений совместно использует ту же блокировку.

8
ответ дан Alex Miller 23 November 2019 в 05:35
поделиться

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

7
ответ дан Dave Ray 23 November 2019 в 05:35
поделиться

Одна классическая проблема изменяет объект, на котором Вы синхронизируетесь при синхронизации на ней:

synchronized(foo) {
  foo = ...
}

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

64
ответ дан Alex Miller 23 November 2019 в 05:35
поделиться

Мой #1 самый болезненный проблема параллелизма когда-либо происходила, когда два различных библиотеки с открытым исходным кодом сделали что-то вроде этого:

private static final String LOCK = "LOCK";  // use matching strings 
                                            // in two different libraries

public doSomestuff() {
   synchronized(LOCK) {
       this.work();
   }
}

На первый взгляд, это похоже на довольно тривиальный пример синхронизации. Однако; потому что Строки , интернировал в Java, литеральная строка "LOCK" оказывается тем же экземпляром java.lang.String (даже при том, что они объявляются полностью разрозненным образом друг от друга.) Результат очевидно плох.

174
ответ дан 2 revs, 2 users 92% 23 November 2019 в 05:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: