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:
We add synchronized
to the add()
and get()
methods and we add synchronized(Test.class)
to the getList()
method;
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:
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.
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.