java:关于多线程的一些特定于案例的问题
问题1强> 如果我们考虑下面的类:
public class Test {
public static LinkedList<String> list;
}
如何使获取/设置变量“list”的线程安全
我想我可以这样做:
public class Test {
private static LinkedList<String> list;
public static synchronized LinkedList<String> getList() {
return new LinkedList<>(list);
}
public static synchronized void setList(LinkedList<String> data) {
list = new LinkedList<>(data);
}
}
问题2强>
但这是线程安全的吗?我是否每次都必须初始化一个新列表,以确保其他副本不会影响变量
问题3强>
如果我们考虑这个问题:
public class Test {
private static LinkedList<String> list;
public static synchronized void ManipulateList() {
// do stuff to 'list'
}
public static synchronized void ChangeList() {
// do more stuff to 'list'
}
}
其中,“操纵列表”和“变更列表”两种方法都可以向同一列表添加或删除变量
这个线安全吗?这是否意味着,如果线程1正在访问“操纵列表”,那么在线程1完成访问“操纵列表”之前,线程2无法访问“变更列表”
我只是不确定我是否正确理解了这些影响
# 1 楼答案
任何子类化您的测试类都可能破坏您的同步方案,因为子类可以直接访问列表,而无需方法同步-通过子类化您的测试类或通过反射
更好的同步解决方案是使用同步包装器初始化列表,如下所示:
在第二个代码段中,您现在可以安全地对测试类进行子类化,并以线程安全的方式访问列表,因为列表本身是同步的
您还可以选择将测试类标记为final,但仍然需要修复实现(在getter和setter中重新初始化列表,这不是一个好主意)
另外,我可能建议您查看一些有关同步的教程,并提出一些建议:
https://www.baeldung.com/java-synchronized-collections
https://howtodoinjava.com/java/collections/arraylist/synchronize-arraylist/
# 2 楼答案
问题1强>
避免全局[可变]状态。把它扔掉
问题2强>
(我将假定这是指
Test.list
而不是传入的data
,由于Java集合库的缺陷,传入的data
本身是可变的因此,您总是使用相同的锁访问列表。当你与外界打交道时,你总是在复制清单。列表的成员是不可变的,因此不需要任何深度复制。一切都好
该方法在不涉及变量的昂贵操作上保持锁定,因此我们应该在这里做得更好
理论上,即使没有更改,锁也不必为整个副本持续保持。因为它是一个公共锁对象(但是很顽皮,但是很普通)
data
可以wait
在它上面暂时释放锁。显然,这并不重要,但在现实世界中,它可能会导致陌生稍微隐晦一点的是
list
可以做成volatile
,锁可以省略问题3强>
对。除此之外,可能存在
wait
,其中一个方法可能会间接调用另一个方法一般注释强>
# 3 楼答案
这不是线程安全的
具体而言,setter:
不强制在
setList
方法期间以独占方式访问data
。因此,其他线程可以在隐式迭代期间修改列表关于列表的更新,问题3中的代码没有问题,因为方法是同步的,这意味着列表是互斥访问的,并且一个方法调用的效果对后续调用是可见的
但这并不是完全安全的,因为恶意代码可以获取(并保持)
Test
的监视器,这可能导致死锁您可以通过使用只能在类内部获取的显式监控器来解决此特定问题: