19

I have two threads. One invokes the update method of a class that modifies a variable. Another invokes the update method of a class that reads the variable. Only one thread writes and one (or more) threads read that variable. What do I need to do in terms of concurrency, since I am new to multi-threading?

public class A
{
    public int variable; // Does this need to be volatile?
       // Not only int, could also be boolean or float.
    public void update()
    {
        // Called by one thread constantly
        ++variable;
        // Or some other algorithm
        variable = complexAlgorithm();
    }
}

public class B
{
    public A a;
    public void update()
    {
        // Called by another thread constantly
        // I don't care about missing an update
        int v = a.variable;
        // Do algorithm with v...
    }
}

Thanks,

Dave
  • 7,283
  • 12
  • 55
  • 101
  • 2
    Many of the answers below assume you are doing integer manipulation that can be handled by the `AtomicInteger` class. For something more complex, look at a `synchronized` block or the `java.util.conccurent.locks.Lock` – justkt Aug 16 '10 at 14:15

4 Answers4

19

If there is one and only one thread that writes to variable you can get away with making it volatile. Otherwise see the answer with AtomicInteger.

Only volatile will work in case of only one writing thread because there is only one writing thread so it always has the right value of variable.

hidralisk
  • 706
  • 6
  • 18
  • That was my point. I know that only one thread modifies it but there could be many threads reading it. – Dave Aug 16 '10 at 14:33
  • @Dave - well, this is why I posted this answer. If your multi-threading is simple, only one thread writes and you have only one variable that it writes to for the other threads to read, you do not need a complex solution, volatile int variable will do. In a general case you would probably want Locks, synchonized or AtomicInteger. But your question was pretty specific on the number of writer threads and number of variables involved. Don't be intimidated by the votes on other answers. If my solution suites your needs, accept it. – hidralisk Aug 17 '10 at 14:35
  • Thanks very much I just needed a second opinion. I also never knew about Atomic variables and other goodies in the concurrency package that I'm happy people here were kind enough to explain. It may prove valuable to me in the future. – Dave Aug 18 '10 at 06:42
9

In this case I would use an AtomicInteger, however the generalised answer is that access to variable should be protected by a synchronized block, or by using another part of the java.util.concurrent package.

A couple of examples:

Using synchronized

public class A {
    public final Object variable;
    public void update() {
        synchronized(variable) {
            variable.complexAlgorithm();
        }
    }
}

public class B {
    public A a;
    public void update() {
        sychronized(a.variable) {
            consume(a.variable);
        }
    }
}

Using java.util.concurrent

public class A {
    public final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public final Object variable;
    public void update() {
        lock.writeLock().lock();
        try {
            variable.complexAlgorithm();
        } finally {
            lock.writeLock().unlock();
        }
    }
}

public class B {
    public A a;
    public void update() {
        a.lock.readLock().lock();
        try {
            consume(a.variable);
        } finally {
            a.lock.readLock().unlock();
        }
    }
}
Jon Freedman
  • 9,469
  • 4
  • 39
  • 58
  • Can you pls explain why not variable.getWriteLock().lock() in class A? – Radu Jul 23 '12 at 08:58
  • You can only synchronize on an object, and that object needs to be final. In this example, the synchronization is done via an implementation of ReadWriteLock (so multiple threads could concurrently read) - see http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html – Jon Freedman Jul 30 '12 at 14:08
8

Not only should variable be volatile, but you also want to protect your update function with some sort of synchronization since ++variable is not an atomic call. It is, after all, just syntactic sugar for

variable = variable + 1;

which is not atomic.

You should also wrap any calls that read variable in a lock of some sort.

Alternatively, use an AtomicInteger. It was made for this sort of thing (for just integer operations).

public class A
{
    // initially had said volatile wouldn't affect this variable because
    // it is not a primitive, but see correction in comments
    public final AtomicInteger variable; // see comments on this issue of why final
    public void update()
    {
        // Called by one thread constantly
        variable.getAndIncrement(); // atomically adds one
    }
    public int retrieveValue()
    {
        return variable.get(); // gets the current int value safely
    }
}

public class B
{
    public A a;
    public void update()
    {
        // Called by another thread constantly
        int v = a.retrieveValue();
        // Do algorithm with v...
    }
}

For the more complex algorithms, as your recent edit assumes, use synchronization or locks.

justkt
  • 14,610
  • 8
  • 42
  • 62
  • By saying "some sort of a lock", you mean "put it in a synchronized block or make the method synchronized" isn't it ? Besides, +1 wih the AtomicInteger – Riduidel Aug 16 '10 at 13:55
  • Or use a `java.util.concurrent.Lock` of some sort. I tend to prefer that over `synchronized` for more expressiveness - I like `ReadWriteLock` especially. – justkt Aug 16 '10 at 13:59
  • 2
    Your comment "volatile won't affect this, it's not a primitive" is misleading. It has nothing to do with whether the field is a primative, but everything to do with the fact that `variable` is no longer being reassigned. Making `variable` final would be advised here. – Mark Peters Aug 16 '10 at 14:04
  • I made some adjustments. What if I also need boolean and float. And what if I don't care about missing updates? – Dave Aug 16 '10 at 14:15
  • @Dave - AtomicBoolean: http://download-llnw.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/atomic/AtomicBoolean.html. There is no AtomicFloat. Consider instead using the synchronization and lock tutorials linked above to lock your access to those variables. If more than one variable represents your state, lock all variables instead of using atomic classes - those only make access to one state object at a time safe. – justkt Aug 16 '10 at 14:18
  • 1
    You don't need synchronization on update if it's done in a single thread and reads are atomic. – starblue Aug 16 '10 at 14:42
  • Where are the comments on this issue regarding the final? – ThirtyOneTwentySeven Dec 30 '21 at 22:15
5

Use AtomicInteger or synchronize the access to be safe.

b_erb
  • 20,932
  • 8
  • 55
  • 64
  • can you please explain what you mean by synchronize? Like where it should go, its a tad confusing for me. – Dave Aug 16 '10 at 14:17
  • @Dave - http://download.oracle.com/javase/tutorial/essential/concurrency/sync.html should help you out with basic Java synchronization. – justkt Aug 16 '10 at 14:20