简体   繁体   中英

Java - Thread safe program

Consider the following piece of code:

public class Test
    {
        private List<Object> list;

        public Object get(int id)
        {
            return list.get(id);
        }

        public void add(Object el)
        {
            list.add(el);
        }

        public List<Object> getList()
        {
            return list;
        }
    }

I have to make sure that it is thread safe (there will be no synchronization errors). I am pretty new to this thing, so my guesses are:

  1. We add synchronized to the add() and get() methods and we add synchronized(Test.class) to the getList() method;

  2. We make list static and all the methods static, then we add synchronized ;

This is what I came up with, it's possible that neither of them are right though.

Your point #1 seems valid. I would put synchronized on get() and add() and getList() , but I would not put anything static, rather return a copy of the list when getList() is called.

Since you do not specify which type, I wrote the snippet with ArrayList as example.

  public synchronized List<Object> getList()
  {
      return new ArrayList<Object>(list);
  }

If you want to emphasise good practices, you could return some sort of immutable collection in order to prevent the "user" of this class from editing the list, believing it may affect the state.

public synchronized List<Object> getList()
{
    return Collections.unmodifiableList(list);
}

synchronize add , get and getList , additionally in getList return UnmodifiableList :

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

public class Test {
    private List<Object> list = new LinkedList<>();

    public synchronized Object get(int id) {
        return list.get(id);
    }

    public synchronized void add(Object el) {
        list.add(el);
    }

    public synchronized List<Object> getList() {
        return Collections.unmodifiableList(list);
    }
}

You need to also synchronize the getList , because Collections.unmodifiableList uses Iterator of the supplied list internally to copy it, so it could give ConcurrentModificationException if not synchronized with the add method.

Using a static modifier means sharing the same list field for all instances of the Test class.
Consequently, your class should be a singleton and it seems not designed to be it.
So, using static doesn't seem necessarily the best way to address your need.

To make your class thread safe, you have to limit the possibility that its state may change concurrently by multiple threads.

So this should be changed :

    public List<Object> getList()

Otherwise the clients may modify concurrently the List instance that is returned.

You should return a unmodifiable list.

You should also synchronize the methods for adding and removing elements to avoid concurrent add or removal operation.

In this way the class is synchronized :

public class Test
    {
        private List<Object> list;

        public synchronized Object get(int id)
        {
            return list.get(id);
        }

        public synchronized void add(Object el)
        {
            list.add(el);
        }

        public synchronized List<Object> getList() {
           return Collections.unmodifiableList(list);
        }

    }

Now if you chain call to these methods, you should also synchronize them since between their call, another thread could change the state of the list.

For example this code :

Test test = ...;
int index = ...;
Object myObject = ...;

if (test.get(index) != myObject){
    test.add(myObject);
}

should be written in this way :

Test test = ...;
int index = ...;
Object myObject = ...;

synchronized(test){
  if (test.get(index) != myObject){
      test.add(myObject);
  }
}

Neither of them is completely right.

We add synchronized to the add() and get() methods and we add synchronized(Test.class) to the getList() method;

Add synchronized to add() , get() and getList() . But adding synchronized(Test.class ) to getList() is wrong since you are adding class lock instead of object level lock to access object variable.

We make list static and all the methods static, then we add synchronized;

This is required if you need only one list across all instances of Test => class level member variable instead of object level member variable. Otherwise, just proceed with instance method level lock instead of class level lock. Follow first approach.

For class level variables, use class level locks. For object level variables, use object level locks.

Refer to oracle pages of Synchronized Methods and Intrinsic Locks and Synchronization for better understanding of concepts.

Few notes from documentation pages:

Object level lock:

Making these methods synchronized has two effects:

  1. First, it is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object.

  2. Second, when a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the state of the object are visible to all threads

Class level lock:

You might wonder what happens when a static synchronized method is invoked, since a static method is associated with a class, not an object. In this case, the thread acquires the intrinsic lock for the Class object associated with the class. Thus access to class's static fields is controlled by a lock that's distinct from the lock for any instance of the class .

That piece of code is thread safe already. Why ? because the global variable is non-static variable. All the thread will create it's own variable List<Object> list; Except if you make List<Object> list; to be a static variable.

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