当前位置: 动力学知识库 > 问答 > 编程问答 >

multithreading - java thread synchronization - this shouldn't be working, but it is :) -

问题描述:

I am following examples from here

I've modified the processCommand as-

private void processCommand() throws InterruptedException {

this.command = "xyz";

}

Full code-

import java.util.logging.Level;

import java.util.logging.Logger;

public class WorkerThread implements Runnable {

private String command;

public WorkerThread(String s) {

this.command = s;

}

@Override

public void run() {

try {

Thread.sleep(1000);

} catch (InterruptedException ex) {

Logger.getLogger(WorkerThread.class.getName()).log(Level.SEVERE, null, ex);

}

System.out.println(Thread.currentThread().getName() + " Commad at start :" + command);

try {

processCommand();

} catch (InterruptedException ex) {

}

System.out.println(Thread.currentThread().getName() + " Command after processCommand : " + command);

}

private void processCommand() throws InterruptedException {

this.command = "xyz";

}

}

Now, I expect to see synchronization issue, right? Basically, when

System.out.println(Thread.currentThread().getName()+' Start. Command = '+command);

is executed, it CAN pick-up the value xyz, right? but I never see it. I've experimented with various values in Thread.Sleep.

So what makes this.command = "xyz"; statement threadsafe in this case?

I am starting thread in this way -

ExecutorService executor = Executors.newFixedThreadPool(5);

for (int i = 0; i < 10; i++) {

Runnable worker = new WorkerThread("" + i);

executor.execute(worker);

}

网友答案:

UPDATE

It is still not entirely what the complete program looks like ... but based on what I think it is, I cannot see any point where it is not thread-safe.

There are two points where command is assigned and two points where the value is read.

  1. The main thread assigns command in the constructor.
  2. A second thread reads command in run() before calling processCommand.
  3. The second thread assigns command in processCommand
  4. The second thread reads command in run() after calling processCommand.

The last three events occur on the same thread, so no synchronization is required. The first and second events occur on different threads, but there should be a "happens before" relation between the main thread and worker thread at that point.

  • If the main thread were to start() the second thread, that would provide the happens before. (The JLS says so.)

  • But actually we are using ThreadPoolExecutor.execute(Runnable) to do the hand-over, and according to the javadoc for Executor:

    Memory consistency effects: Actions in a thread prior to submitting a Runnable object to an Executor happen-before its execution begins, perhaps in another thread.

In summary, all 4 events of interest are properly synchronized, and there are no race conditions involving command.


However, even if this was not thread-safe you would have difficulty demonstrating that non-thread-safe behaviour.

  • The main reason you cannot demonstrate it is that the actual non-safeness is due to Java memory model. Changes to the command variable only need to be flushed to main memory if there is synchronization point or something to establish the "happens before". But they can be flushed anyway ... and they usually are ... especially if there is a long enough time gap, or a system call that causes a context switch. In this case you have both.

  • A second reason is that the System.err and System.out objects are internally synchronized, and if you are not careful with the way you call them you can eliminate the thread-safety problem you trying to demonstrate.


This is "the thing" about thread-safety issues involving non-synchronised access to shared variables. The actual race conditions often involve very small time windows; i.e. two events that need to happen within a few clock cycles (certainly less than a microsecond) for the race to be noticed. This is likely to happen rarely, which is why problems involving race conditions are typically so hard to reproduce.

网友答案:

The reason you don't see a race condition here is

Runnable worker = new WorkerThread('' + i);

A race condition involves a shared resource. All your worker threads on the other hand are changing their own private member command. To induce a race condition you would need to do something like

for (int i = 0; i < 10; i++) {
  Runnable worker = new WorkerThread('' + 0);    
  executor.execute(worker);
  worker.setCommand('' + i);
}

Now when the worker tries to access the command field it could get the stale 0 value or the i value.

分享给朋友:
您可能感兴趣的文章:
随机阅读: