arn-arn
arn-arn

Reputation: 1377

how to prevent happens-before in synchronized code in java?

I only have one synchronized method. The purpose of which is to prevent two threads from updating the same table which will surely cause an exception. I am trying to update one table whose data is from a huge external db. Before updating my table, I have to do some filtering and cleaning up which takes a lot of time. I created multiple threads so that I can divide the tasks and update the table fast. The plan/design is as soon as one thread is finished with cleaning up the data it should update my table while the other threads are still working on cleaning up. However, in my sample code, the threads are overwriting the values because of happens-before relationship. I don't want the threads to share the data i just want them to not update while some thread is updating. If you can see in my code below, I have created a list which i use to loop thru to create a thread and pass each data from the list. However, in the result, the thread 1, 4 and 5 are using the same programId in the Synchronized method when they should not be. Can anyone help to prevent this so that each thread will have a unique Id in the Synchronized code?

Here is my code:

public Class HomeController{

    List<String> programList = new ArrayList<String>();
    programList.add("xxx");
    programList.add("yyy");
    for(String programId: programList){

    System.out.println("programId Controller is " + programId);
    //for each program Id create a thread
    runnable.setProgramId(programId);
    taskExecutor.execute(runnable);
    }

}

public class TestRunnable implements Runnable{

    public void run(){
            String threadName = Thread.currentThread().getName();
            System.out.println("threadName and programId Runnable are " + threadName +"--"+ programId);
            TestSynchronized testSynchronized = new TestSynchronized();
            testSynchronized.updateTable(programId);
            }
    }

}

public class TestSynchronized{

    private volatile boolean isLocked = false; 

    public synchronized void updateTable(String programId){

            while(isLocked) {
                 try {
                     wait();
                 } catch (InterruptedException e) {}
            }
            isLocked=true;
            String threadName = Thread.currentThread().getName();
            System.out.println("threadName and programId Synchronized are " + threadName +"--"+ programId);

            isLocked=false;
            notify();
    }
}

Here is the result:

programId Controller is 23022490857
programId Controller is 23022491963
threadName and programId Runnable are taskExecutor-1--23022490857
programId Controller is 23022492068
threadName and programId Runnable are taskExecutor-2--23022491963
programId Controller is 23022492300
threadName and programId Synchronized are taskExecutor-1--23022491963
programId Controller is 23022495561
threadName and programId Runnable are taskExecutor-3--23022492068
threadName and programId Synchronized are taskExecutor-2--23022492068
programId Controller is 38416827134
threadName and programId Synchronized are taskExecutor-3--23022492300
threadName and programId Runnable are taskExecutor-4--23022492300
threadName and programId Runnable are taskExecutor-1--38416827134
threadName and programId Synchronized are taskExecutor-1--38416827134
threadName and programId Runnable are taskExecutor-5--23022495561
threadName and programId Synchronized are taskExecutor-4--38416827134
threadName and programId Synchronized are taskExecutor-5--38416827134

Upvotes: 0

Views: 118

Answers (4)

Solomon Slow
Solomon Slow

Reputation: 27190

...the threads are overwriting the values because of happens-before relationship.

Not really answering your question but, I think you misunderstand the meaning of "Happens Before".

The Java Language Spec (JLS) uses the phrase "happens before" in describing certain guarantees that you can use in reasoning about how your program will behave. For example, the JLS says that leaving a synchronized(o) block in one thread happens before another thread synchronizes on same object o. That's how you know that if thread A enters the block, updates some variable, and then leaves the block, and then thread B enters the block; thread B is guaranteed to see what thread A stored in the variable.

But here's what it doesn't tell you:

It doesn't tell you which thread is going to get in to the synchronized block first. If it's possible for two threads in your program to attempt to synchronize on the same object at the same time, then you've got a data race. One thread is going to win, the other is going to lose, and there's no way to know which one will win.

If you've got a data race in your program, and it matters which thread wins, then you've got a defect.

It's up to you to either change your design so that it doesn't matter which thread wins, or change your design to use some kind of higher-level synchronization (queues, semaphores, barriers, etc.) to insure that the right thread wins.

Upvotes: 1

Mark Rotteveel
Mark Rotteveel

Reputation: 109144

You have two problems in the code shown:

  1. You are reusing the same TestRunnable for each thread, this means that the programId is (or can be) overwritten before you use it.
  2. You are creating a new TestSynchronized for each thread, so you have no thread safety at all.

What you need to do is:

  1. Create one, and only one TestSynchronized
  2. Create a new TestRunnable for each programId. Pass the programId and the shared TestSynchronized in the constructor.

Upvotes: 2

John Vint
John Vint

Reputation: 40256

You need to understand how object monitors work. synchronized will block access to the resource, the object reference. In this case you create a new TestSynchronized for each Thread. Since synchronizing works on the object reference, you are essentially not blocking anywhere.

If you create one TestSynchronized and pass it to each TestRunnable you will get the output you expect.

Upvotes: 1

Tassos Bassoukos
Tassos Bassoukos

Reputation: 16152

How many copies of TestSynchronized do you have? If it's more than one you're going to not get any synchronization.

Separate the synchronization with the actual code execution.

Upvotes: 1

Related Questions