简体   繁体   中英

Java thread safety - multiple atomic operations?

I'm just a non-developer playing to be a developer, so my question may be extremely simple!

I'm just testing Java multi-threading stuff, this is not real code. I wonder how to make two member variables update at the same time in Java, in case we want them both in sync. As an example:

public class Testing
{
  private Map<String, Boolean> itemToStatus = new ConcurrentHashMap<>();
  private Set<String> items = ConcurrentHashMap.newKeySet();

  public static void main(String[] args)
  {
    (new Testing()).start("ABC");
  }

  public void start(String name) {
    if (name.equals("ABC")) {
      itemToStatus.put(name, true);
      items.add(name);
    }
  }
}

In that scenario (imagine multi-threaded, of course) I want to be able to guarantee that any reads of items and itemToStatus always return the same.

So, if the code is in the line itemToStatus.put(name, true) , and other thread asks items.contains(name) , it will return false. On the other hand, if that other thread asks itemToStatus.containsKey(name); it will return true. And I don't want that, I want them both to give the same value, if that makes sense?

How can I make those two changes atomic? Would this work?

if (name.equals("ABC")) {
    synchronised(this) {
        itemToStatus.put(name, true);
        items.add(name);
    }
}

Still, I don't see why that would work. I think that's the case where you need a lock or something?

Cheers!

Just synchronizing the writes won't work. You would also need to synchronize (on the same object) the read access to items and itemToStatus collections. That way, no thread could be reading anything if another thread were in the process of updating the two collections. Note that synchronizing in this way means you don't need ConcurrentHashMap or ConcurrentHashSet ; plain old HashMap and HashSet will work because you're providing your own synchronization.

For example:

public void start(String name) {
    if (name.equals("ABC")) {
        synchronized (this) {
            itemToStatus.put(name, true);
            items.add(name);
        }
    }
}

public synchronized boolean containsItem(String name) {
    return items.contains(name);
}

public synchronized boolean containsStatus(String name) {
    return itemToStatus.containsKey(name);
}

That will guarantee that the value returned by containsItem would also have been returned by containsStatus if that call had been made instead. Of course, if you want the return values to be consistent over time (as in first calling containsItem() and then containsStatus() ), you would need higher-level synchronization.

The short answer is yes: by synchronizing the code block, as you did in your last code snippet, you made the class thread-safe because that code block is the only one that reads or modifies the status of the class (represented by the two instance variables). The meaning of synchronised(this) is that you use the instance of the object ( this ) as a lock: when a thread enters that code block it gets the lock, preventing other threads to enter the same code block until the thread releases it when it exits from the code block.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM