考虑以下代码:
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;
}
}
我必须确保它是线程安全的(不会出现同步错误)。我对这件事情很陌生,所以我的猜测是:
我们将synchronized
添加到add()
和get()
方法,我们将synchronized(Test.class)
添加到getList()
方法;
我们将list
设为静态且所有方法都是静态的,然后我们添加synchronized
;
这就是我想出来的,虽然它们都不是正确的但是可能。
答案 0 :(得分:3)
你的观点#1似乎有效。我会将synchronized
放在get()
和add()
以及getList()
上,但我不会放任何静态内容,而是在调用getList()
时返回列表的副本
由于您没有指定哪种类型,我以ArrayList
为例编写了代码段。
public synchronized List<Object> getList()
{
return new ArrayList<Object>(list);
}
如果你想强调好的做法,你可以返回某种不可变的集合,以防止这个类的“用户”编辑列表,相信它可能会影响状态。
public synchronized List<Object> getList()
{
return Collections.unmodifiableList(list);
}
答案 1 :(得分:2)
同步add
,get
和getList
,另外在getList
返回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);
}
}
您还需要同步getList
,因为Collections.unmodifiableList
在内部使用提供的列表的Iterator
来复制它,因此如果不是ConcurrentModificationException
,它可以提供synchronized
使用add
方法。
答案 2 :(得分:2)
使用静态修饰符意味着为Test类的所有实例共享相同的list
字段
因此,你的班级应该是一个单身人士,而且似乎不是设计成它
因此,使用静态似乎无法满足您的需求。
为了使您的类线程安全,您必须限制其状态可能由多个线程同时更改的可能性。
所以应该改变:
public List<Object> getList()
否则客户端可能会同时修改返回的List实例。
您应该返回一个不可修改的列表。
您还应该同步添加和删除元素的方法,以避免并发添加或删除操作。
通过这种方式,类同步:
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);
}
}
现在如果你对这些方法进行链接调用,你也应该同步它们,因为在它们的调用之间,另一个线程可能会改变列表的状态。
例如这段代码:
Test test = ...;
int index = ...;
Object myObject = ...;
if (test.get(index) != myObject){
test.add(myObject);
}
应该用这种方式写出来:
Test test = ...;
int index = ...;
Object myObject = ...;
synchronized(test){
if (test.get(index) != myObject){
test.add(myObject);
}
}
答案 3 :(得分:1)
它们都不完全正确。
我们将synchronized添加到add()和get()方法,并将synchronized(Test.class)添加到getList()方法;
将同步添加到add()
,get()
和getList()
。但是将synchronized(Test.class
)添加到getList()
是错误的,因为您要添加类锁而不是对象级锁来访问对象变量。
我们使列表静态并且所有方法都是静态的,然后我们添加synchronized;
如果您只需要Test
=&gt;的所有实例中的一个列表,则必须执行此操作类级别成员变量而不是对象级别成员变量。否则,只需继续实例方法级别锁定而不是类级别锁定。遵循第一种方法。
对于类级变量,请使用类级锁。对于对象级变量,请使用对象级锁。
请参阅Synchronized Methods和Intrinsic Locks and Synchronization的oracle页面,以便更好地理解概念。
文档页面中的一些注释:
对象级锁定:
制作这些方法synchronized
有两个影响:
首先,对同一对象的两个synchronized
方法的调用不可能进行交错。当一个线程正在为一个对象执行synchronized
方法时,所有其他线程都会为同一个对象阻塞synchronized
方法(暂停执行),直到第一个线程完成该对象为止。
其次,当synchronized
方法退出时,它会自动为同一对象的synchronized
方法的任何后续调用建立一个before-before关系。这可以保证所有线程都可以看到对象状态的更改
班级锁定:
您可能想知道在调用静态synchronized
方法时会发生什么,因为static
方法与类关联,而不是与对象关联。在这种情况下,线程获取与类关联的Class
对象的内部锁。 因此,对类的静态字段的访问由一个锁定控制,该锁定与该类的任何实例的锁不同。
答案 4 :(得分:0)
那段代码已经是线程安全的。为什么 ?因为全局变量是非静态变量。所有线程都会创建它自己的变量 List<Object> list;
除非您将 List<Object> list;
设为 静态 变量。