2012-05-22 1 views
1

Если у меня есть класс, который реализует java.lang.Runnable, технически ничего не остановить несколько потоков от вызова run одновременно на то же экземпляр Runnable. Пример:Должны ли реализации Runnable допускать одновременные вызовы run() в одном экземпляре?

Runnable r = new MyRunnable(); 

//start thread 1 
new Thread(r).start(); 

//start thread 2 
new Thread(r).start(); 

Это не проблема, если метод run полностью «самодостаточный» (т.е. не зависит от каких-либо частных членов экземпляра), потому что нет переменных разделяемых между потоками, так что ваш Runnable никогда не может получить «поврежденное» внутреннее состояние. Но я думаю, что в большинстве случаев у вашего будет есть некоторые частные члены экземпляра, которые он манипулирует в run, так что делать?

Чтобы использовать пример, мой класс MyRunnable имеет поле mode. Основная цель состоит в том, чтобы чисто остановить метод run (установив mode на STOPPED), но он также имеет другие виды использования.

public static class MyRunnable implements Runnable 
{ 
    enum Mode { RUNNING, PAUSED, STOPPED, FASTFORWARDING, REWINDING; } 

    private volatile Mode mode; 

    @Override 
    public void run() 
    { 
     mode = Mode.RUNNING; 
     while (mode != Mode.STOPPED) 
     { 
      //do stuff. possibly other changes to the mode. 
     } 
    } 

    public void setMode(Mode mode) 
    { 
     this.mode = mode; 
    } 

    public Mode getMode() 
    { 
     return mode; 
    } 
} 

Как вы можете видеть, если я исполню несколько потоков одновременно для же экземпляра MyRunnable (как показано выше), любые изменения в mode может повлиять одновременно запущенных потоков по-разному. Например, если поток устанавливает mode в STOPPED, другой поток может изменить mode на что-то еще, прежде чем каждый другой поток даже увидит, что он установлен в STOPPED. Это может легко предотвратить поток, выходящий из цикла while.

Итак, как мне создать Runnable как «одновременно применимый»? Возможно ли это? Разве это не стоит? Есть ли что-то вроде атрибута C# ThreadStatic, который я могу использовать (который, я думаю, был бы идеальным в этом контексте)?

ответ

9

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

//start thread 1 
new Thread(new MyRunnable()).start(); 
//start thread 2 
new Thread(new MyRunnable()).start(); 

Вся цель иметь несколько потоков, работающих, чтобы достичь хорошего параллельных вычислений. Если потоки должны блокировать общие ресурсы (например, рабочие очереди и т. Д.), Тогда штраф, но запись ваших Runnable s, чтобы сделать их «одновременно пригодными для использования», не должно быть сделано, как само собой разумеющееся - для этого должны быть веские причины.

Возможно ли это?

Конечно, это возможно. Темы разделяют ресурсы все время между ними.Однако в вашем случае с переменной Mode mode это должно быть сделано с несколькими экземплярами вашего класса Runnable.

Если несколько потоков вызывают общий код, один шаблон, который вы можете рассмотреть, использует ThreadLocal для хранения информации о потоках. Но хранение полей внутри нескольких экземпляров вашего MyRunnable будет более эффективным, чем наличие одного экземпляра MyRunnable с mode, хранящимся в ThreadLocal.

Разве это не стоит?

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

0

В целом, это не стоит.

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

Вы можете, конечно, также использовать переменные ThreadLocal, которые в целом невероятно полезны, но, вероятно, не в этом сценарии.

1

Точно так же вы создаете любой другой метод или класс для потокобезопасности. С Runnable нет ничего принципиально иного.

C# ThreadStatic является эквивалентом ThreadLocalVariable, но независимо от того, является ли это подходящим решением здесь, мы все знаем, потому что нам не нужно знать, какое поведение вы хотите, чтобы ваш класс соответствовал.

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

0

Ну, я не могу сказать, что у меня много опыта работы с MP на Java, поэтому я не буду пытаться говорить об общем использовании, но после чтения документов Runnable и Thread это звучит как Runnable. именно то, что подразумевается в названии: runnable object. В частности, в документах упоминается использование Runnable в тех случаях, когда вы не хотите внедрять Thread, но хотите аналогичную концепцию. Я действительно думаю, что делает Runnable идеальным для классов, которые инкапсулируют параллельные или параллельные алгоритмы. Разумеется, нет ничего, что подразумевало бы, что вы не должны или не можете специально разрабатывать Runnable для параллелизма. Конечно, это само по себе (в зависимости от задачи) гораздо больше, чем может решить один вопрос SO.

Редактирование: я должен уточнить, я упомянул инкапсуляцию параллельных или параллельных алгоритмов в Runnable, но я хотел сказать, что также может иметь смысл инкапсулировать задачу в Runnable, а затем позволить вызывающему абоненту определить, следует ли выделять параллель ресурсов для этой задачи, например, вызывая его в параллельных потоках. Моя точка зрения заключается в том, что, как полагают документы, если вы можете представить объект, который выполняется (в любом контексте), то Runnable может быть хорошей ставкой!

0

Я думаю, что вопрос OP находится в определенных сценариях, когда Runnable является ресурсом тяжелым, есть ли способ избежать воссоздания этих Runnables?

Мой опыт в том, что вы всегда можете создавать легкие Runnables, чтобы ссылаться на тяжелые весовые ресурсы (например, огромные массивы и т. Д.). Избегайте вызывать одни и те же Runnables для нескольких потоков, но создать другой Runnable для каждого потока лучше, на мой взгляд.

Смежные вопросы