2016-11-08 3 views
1
object GetDetails{ 
    def apply(outStream: java.io.Writer, someObject: SomeClass){ 
    outStream.write(someObject.detail1) //detail1 is string 
    outStream.write(someObject.detail2) //detail2 is also string 
    outStream.flush() 
    } 
} 

Если эта реализация не является потокобезопасной, как мне сделать ее потокобезопасной?Scala- Как определить, является ли объект потокобезопасным?

Эта функция будет вызываться одновременно с различными входами.

+0

До тех пор, пока входы различаются (и не подключены к тому же потоку внизу), вы в порядке. – Dima

ответ

0

О чем можно подумать, так как «состояние» вашего приложения может измениться, когда два разных потока вызовут одну и ту же функцию или взаимодействуют с одним и тем же объектом. В этом случае ваше «состояние» может быть «тем, что было написано до outStream».

Рассмотрим следующий сценарий:

Thread 1         Thread 2 
--------------------      ------------------------------- 
GetDetails(outStream, object1) 
             GetDetails(outStream, object2) 
    outStream.write(object1.detail1) 
              outStream.write(object2.detail1) 
    outStream.write(object1.detail2) 
              outStream.write(object2.detail2) 
              outStream.flush() 
    outStream.flush() 

Две отдельные темы, как вызов GetDetails, разделяя те же outStream. Это потенциальная проблема параллелизма, поскольку данные, которые записываются в outStream, не гарантируются в каком-либо конкретном порядке. Вы можете получить [object1.detail1, object2.detail1, object1.detail2, object2.detail2], или [object2.detail1, object1.detail1, object1.detail2, object2.detail2], и так далее.

GetDetails.apply не изменяет состояние GetDetails, но он изменяет состояние Writer, которое вы передаете; чтобы обеспечить безопасность потоков, вы должны предпринять усилия, чтобы избежать одновременного использования одного и того же Writer (т. е. сценария выше).

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

object NotThreadSafe { 
    // mutable state 
    private var currentPrefix = "" 

    def countUp(prefix: String) = { 
    // red flag: changing mutable state, then referring to it 
    currentPrefix = prefix 
    for(i <- 1 to 5) println(s"$currentPrefix $i") 
    } 
} 

Если нить 1 вызовы NotThreadSafe.countUp("hello") и поток 2 вызовов NotThreadSafe.countUp("goodbye"), выход будет зависеть от того, currentPrefix = prefix бывает последним.

Вы могли бы в конечном итоге с

привет 1
привет 2
прощай 3 // должно быть привет 3, но currentPrefix переоделся
прощай 1
распрощаться 4 // должно быть привет 4
до свидания 5 // должно быть привет 5
до свидания 2
до свидания 3
до свидания 4
до свидания 5

или один из многих других перестановок.

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

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

Вы также можете посмотреть semaphores, но это выходит за рамки этого ответа.

+0

Спасибо за ответ. Избавьтесь от потоков и функция вернет безопасность потока List [String]? –

+0

@AnIllusion Если ваш метод 'getDetails' не влияет на какое-либо состояние за пределами собственных локальных vals/vars, тогда да. Если все, что вы делаете, создает возвращаемый «Список», то да, потому что вы не влияете на какое-либо другое состояние. – Dylan

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