Anand
Anand

Reputation: 551

Java ExecutorService and Synchronization

I am trying to understand the ExecutorService and how it will avoid the usage of synchronized keyword. I tried something like this below (avoiding imports to for clarity) which gave wrong results:

public class ReadWebPage
{
   public static void main(String[] args)
   {

      ExecutorService executor = Executors.newFixedThreadPool(5);

      FinTrans ft = new FinTrans ();
      TransThread tt1 = new TransThread (ft, "Deposit Thread");
      TransThread tt2 = new TransThread (ft, "Withdrawal Thread");

      executor.submit(tt2);
      executor.submit(tt1);

      executor.shutdown();
    }
}

class FinTrans
{
   public static String transName;
   public static double amount;
}
class TransThread implements Runnable
{
   private FinTrans ft;
   private String name;
   TransThread (FinTrans ft, String name)
   {
      this.name = name;
      this.ft = ft;
   }
   public String getName(){
  return name;
   }
   public void run ()
   {
      for (int i = 0 ; i < 10 ; i++ )
      {
           if (getName ().equals ("Deposit Thread"))
           {
               ft.transName = "Deposit";
               try
               {
                  Thread.sleep ((int) (Math.random () * 100));
               }
               catch (InterruptedException e)
               {
               }
               ft.amount = 2000.0;
               System.out.println (ft.transName + " " + ft.amount);
           }
           else
           {
               ft.transName = "Withdrawal";
               try
               {
                  Thread.sleep ((int) (Math.random () * 10));
               }
               catch (InterruptedException e)
               {
               }
               ft.amount = 250.0;
               System.out.println (ft.transName + " " + ft.amount);
           }
      }
   }
}

Which gave me the wrong result:

Deposit 250.0          //wrong
Withdrawal 250.0
Withdrawal 250.0
Withdrawal 250.0
Withdrawal 250.0
Withdrawal 250.0
Withdrawal 250.0
Withdrawal 250.0
Withdrawal 250.0
Withdrawal 2000.0      //wrong
Deposit 250.0          //wrong
Deposit 2000.0
Deposit 2000.0
Deposit 2000.0
Deposit 2000.0
Deposit 2000.0
Deposit 2000.0
Deposit 2000.0
Deposit 2000.0
Deposit 2000.0

Could you please explain what is going wrong here? 1. Is it because both the Runnables share the same FinTrans object? 2. Does this mean we need same old synchronize mechanism here too?

I am trying to understand at what situation we can use the ServiceExecutors

Anand

Upvotes: 1

Views: 3174

Answers (2)

mavarazy
mavarazy

Reputation: 7735

You need to have synchronous blocks, in case of asynchronous processing. If you are trying to emulate bank account, processing in java, you can, for example, use some java concurrent primitives.

With AtomicDouble, you can do it like this:

class FinTrans {
   final private AtomicDouble amount = new AtomicDouble();

   public void debit(double debit) {
      double current = 0;
      double new = 0;
      do {
         current = amount.get();
         new = current + debit;
      } while (!amount.compareAndSet(current, newAmount));
   }

   public void credit(double credit) {
     debit(- credit);
   }
}

Upvotes: 0

William Price
William Price

Reputation: 4102

ExecutorService runs jobs in parallel, but does not automatically make it safe to concurrently access shared state across threads. So, "yes" to both parts of your question.

  1. The problem is caused, in part, by the two runnables sharing the same FinTrans state object.
  2. The other part of the problem is the lack of any synchronization or otherwise atomic guarantees around accesses to that shared state.

The executors are good for submitting tasks asynchronously and being able to check on their status (e.g. have they finished) via the returned Future instances.

Upvotes: 2

Related Questions