0

I'm having an issue that cropped up in multithreading. I have an enum with state set through setters instead of the constructor. I'll have 2 threads that make instances of this enum for different uses. Each thread will have its own use for this setter. Each thread will have multiple instances of these enums floating around.

My problem is that when I call the setter in one thread and it's immediately called in another thread, the state in the initial enum is overwritten by what was provided to the setter in the second enum.

I have provided a MWE below. How do I fix this issue?

This isn't just a problem in a multi-threaded context, but I would like the solution to be thread-safe as well.

Note also that I use reference based testing, e.g. if(enumValue == TestEnumMutate.A) must return true if enumValue is indeed A.

enum TestEnumMutate {
  A(0),
  B(0);

  int i;

  TestEnumMutate(int i) {
    this.i=i;
  }

  public void setI(int i) {
    this.i=i;
  }

  public void print() {
    System.out.println(i);
  }
}

class TestClassMutate {
  private int i = 0;

  public TestClassMutate() { }

  public void setI(int i) {
    this.i=i;
  }

  public void print() {
    System.out.println(i);
  }
}

public class ThreadSafeTest implements Runnable{
  private boolean sleep;
  private boolean isTestingEnum;

  public ThreadSafeTest(boolean sleep,boolean isTestingEnum) {
    this.sleep=sleep;
    this.isTestingEnum=isTestingEnum;
  }

  public void run() {
    if(isTestingEnum) {
      this.runEnumTest(sleep);
    } else {
      this.runClassTest(sleep);
    }
  }

  private void runClassTest(boolean sleep) {
    TestClassMutate foo = new TestClassMutate();

    if(sleep) {
      try {
        Thread.sleep(500);
      } catch(Exception e) {};
    } else {
      foo.setI(1);
    }

    foo.print();
  }

  private void runEnumTest(boolean sleep) {
    final TestEnumMutate foo = TestEnumMutate.A;

    if(sleep) {
      try {
        Thread.sleep(500);
      } catch(Exception e) {};
    } else {
      foo.setI(1);
    }
    foo.print();
  }

  public static void main(String[] args) throws Exception {
    System.out.println("Should print 1 then 0 if one thread's instance didn't"+
      " interfere with the other thread's instance");
    Thread enumTestThread1 = new Thread(new ThreadSafeTest(true,true));
    Thread enumTestThread2 = new Thread(new ThreadSafeTest(false,true));

    enumTestThread1.start();
    enumTestThread2.start();

    enumTestThread1.join();
    enumTestThread2.join();

    System.out.println("Should print 1 then 0 if one thread's instance didn't"+
      " interfere with the other thread's instance");
    Thread classTestThread1 = new Thread(new ThreadSafeTest(true,false));
    Thread classTestThread2 = new Thread(new ThreadSafeTest(false,false));

    classTestThread1.start();
    classTestThread2.start();

    classTestThread1.join();
    classTestThread2.join();
  }
}
dimo414
  • 47,227
  • 18
  • 148
  • 244
user2763361
  • 3,789
  • 11
  • 45
  • 81
  • ...Why not use an `AtomicInteger`? – Unihedron Jul 09 '14 at 03:25
  • @Unihedron This could be an answer. Do you mind posting an example? – user2763361 Jul 09 '14 at 03:27
  • 4
    You cannot have multiple instances of an enum. That's a large part of the point of enums. When your two threads call the setter on your enum constant, they are calling it on the same instance. – Kevin Krumwiede Jul 09 '14 at 03:31
  • @KevinKrumwiede Is there a standard alternative choice when I need enum-like functionality over multiple instances as well as state that differs between instances? – user2763361 Jul 09 '14 at 03:32
  • 2
    @user2763361 - it might help to elaborate on what "enum-like functionality" you think you need. Wanting to change enum state concurrently speaks to deeper design mistakes. – dimo414 Jul 09 '14 at 03:53
  • 1
    What @dimo414 said. "Enum-like functionality over multiple instances" is self-contradictory. – Kevin Krumwiede Jul 09 '14 at 15:31

2 Answers2

3

Note that you're effectively describing conflicting requirements - an enum provides type-safe singletons; you cannot have multiple instances of the same enum value. Are you perhaps looking for the behavior EnumMap provides, the ability to map enums to different values? You could then have each thread or class hold its own mapping. This way the enums stay constant but the values based on the enum can change at will.

An alternative would be to use a ThreadLocal, this lets you specify a unique value per-thread (under the covers, it's essentially a map, like above), so that multiple threads don't interrupt each other.

While an AtomicInteger, as Unihedron suggests, would ensure thread-safe incrementing, it's not clear to me that is what you're actually trying to do; if I understand correctly you don't want the behavior of one thread to impact the behavior of another, which means you simply shouldn't be storing this state in your enums at all.

Community
  • 1
  • 1
dimo414
  • 47,227
  • 18
  • 148
  • 244
  • Yeah, let's map `enum` values to `AtomicInteger` with `EnumMap`. Our answers are perfect complements of each other! – Unihedron Jul 09 '14 at 03:53
1

You may look into the use of an AtomicInteger instead of using an enum or a mutable object to wrap a value:

class MyWrapper {
    AtomicInteger aInt = new AtomicInteger(0);
}

within threads, you can use the following, thread-safely:

{
    MyWrapper.aInt.set(12);
    MyWrapper.aInt.addAndGet(1);
    // ...
}

As all AtomicInteger objects are mutable, this does what you will need. Also, enums are immutable, so they will not perform what you need.

Unihedron
  • 10,902
  • 13
  • 62
  • 72