Here are the question and answer
val stopMe = new Runnable {
private var stop = false
override def run() { while (!stop) doSomething(); println("Stopped") }
def stopTask() { stop = true }
}
new Thread(stopMe).start()
Answer:- no synchronization, no guarantee that the value changed for "stop" will ever be seen
- stop is not volatile so jvm spec cant guarantee the changed stop value to be visible among threads
class MyTask implements Runnable {
private val done = new ArrayBlockingQueue[String](1)
private val stop = new AtomicBoolean
public void run() {
while (!stop.get()) doSomething()
done.put("DONE")
}
public void stopNow() {
stop.set(false)
done.take() // Wait until run completes
}
}
Answer:- if doSomething() call stopNow(), then...deadlock?
- stop.set(false) invocation should be stop.set(true)
class BackgroundTask(iters: Int) extends Runnable {
override def run() { for (i <- 1 to iters) doSomething() }
}
new Thread(new BackgroundTask(1000)).run()
println("Started backgroundTask")
Answer:- should call "start()" instead of "run()". I do that all the time
val items = new ConcurrentHashMap[String, Int]
...
val keys = items.keySet().toArray
Answer:- values when keySet() is called may not be the same when toArray is finished, therefore, result maybe unintended
- val keys = items.keySet().toArray has no sense if it is not run in keys.synchronized block - other threads may change items and toArray will throw ConcurrentModificationExce
ption
class Stack {
private val myLock = "LOCK"
def push(newValue: Int) {
myLock.synchronized {
...
}
}
}
Answer:- String constant is shared in Java, all Stack will use the same "LOCK"
- "LOCK" String is pointing to instance in JVM`s internal strings cache (somewhere in String class) - this synchronized method may cause much "wider" synchronization than it seems (global lock on all non-new "Lock" Strings)
class Stack {
...
def push(newValue: Int) {
new String("LOCK").synchronized {
...
}
}
}
Answer:- new.......every push call creates a new lock object......nothing is really synchronized with it
class Stack {
private var values = new Array[Int](10)
private var size = 0
def push(newValue: Int) {
values.synchronized {
if (size > values.size) reallocate()
values(size) = newValue
size += 1
}
}
...
private def reallocate() {
values = values ++ new Array[Int](values.size)
}
}
Answer:- everytime "reallocate()" is called the reference of "values" changes, therefore similar to "releasing" the lock unintentionally
class Stack { private val myLock = new ReentrantLock private val cond = myLock.newCondition() ... def pop() = { myLock.lock() try { do { cond.await() } while (size == 0) size -= 1 values(size) } finally { myLock.unlock() } } }Answer:
- should always do while-do instead of do-while
- A do/while is used for the condition variable rather than just while. If cond is in a signaled state initially, the condition that size is zero may not be checked
class Stack {
private val myLock = new ReentrantLock
private var values = new Array[Int](10)
private var size = 0
...
def write(fileName: String) {
myLock.lock()
val out = new PrintWriter(fileName)
try {
out.println(size + " " + values.mkString(" "))
} finally {
out.close()
myLock.unlock()
}
}
}
Answer:- out.close() might throw exception, causing myLock.unlock() to never get called
val queue = new ArrayBlockingQueue[String](10)
val button = new JButton("Start")
button.addActionListener(new ActionListener {
override def actionPerformed(event: ActionEvent) {
queue.put(event.toString)
}
}
Answer:- if queue.put is blocked, as queue is full, the main UI thread will be blocked
class Model {
private val myLock = new ReentrantLock
private def withMyLock(block: => Unit) {
myLock.lock(); try { block } finally { myLock.unlock() ; }
}
private val listeners = new ArrayBuffer[ChangeListener]
def addListener(l: ChangeListener) { withMyLock { listeners += l } }
def removeListener(l: ChangeListener) { withMyLock { listeners -= l } }
def fireListeners() {
withMyLock {
for (l <- listeners) l.stateChanged(new ChangeEvent(this))
}
}
...
}
Answer:- since ChangeListener is implemented by other ppl, if they call addListener or removeListener in there listener then unexpected behavior might occur (in fireListeners)
- should use CopyOnWriteArrayList in order to allow a listener to call add/remove listener when the event is being dispatched without causing a CME and/or deadlock
val queue = new ArrayBlockingQueue[String](10)
val formatter = new SimpleDateFormat("MMM dd HH:mm:ss")
class MyTask extends Runnable {
override def run() {
Object result = doSomething()
queue.put(formatter.format(new Date) + " " + result)
}
}
for (i <- 1 to 10) { new Thread(new MyTask).start() }
Answer:- simple....................because SimpleDateFormat is too simple to run in multi-thread.......lol. refer to JavaDoc
class Stack(val maxSize: Int) {
private val array = new ArrayDeque[Int]
def put(value: Int) {
array.synchronized {
while(array.size == maxSize) {
wait()
}
array.add(value)
}
}
...
}
Answer:- synchronizes on this.array but waits on this = IllegalMonitorStateExcepti
on
Some Other:
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 ...
}
}
Answer:- Servlet is Singleton in a container. Mutating a instance field will result in unexpected behavior of other process on running this Servlet
Double-Checked Locking idiom
// Broken multithreaded version
// "Double-Checked Locking" idiom
class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null) {
synchronized(this) {
if (helper == null) {
helper = new Helper();
}
}
}
return helper;
}
// other functions and members...
}
Answer:
- When thread A is creating a new instance of Helper, thread B may come in and see that "helper" is no longer null and uses the not-fully-instantiated instance