Java 在同一锁上同步
我正在读的一本书的摘录- //在错误的锁上同步,因此@NotThreadSafeJava 在同一锁上同步,java,multithreading,synchronization,Java,Multithreading,Synchronization,我正在读的一本书的摘录- //在错误的锁上同步,因此@NotThreadSafe public class ListHelper<E> { public List<E> list = Collections.synchronizedList(new ArrayList<E>()); public synchronized boolean putIfAbsent(E x){ boolean absent = !list.co
public class ListHelper<E> {
public List<E> list = Collections.synchronizedList(new ArrayList<E>());
public synchronized boolean putIfAbsent(E x){
boolean absent = !list.contains(x);
if(absent)
list.add(x);
return absent;
}
}
公共类ListHelper{
public List=Collections.synchronizedList(新的ArrayList());
公共同步布尔putIfAbsent(E x){
布尔缺席=!list.contains(x);
如果(缺席)
增加(x);
缺席返回;
}
}
//在相同/正确的锁上同步,因此@ThreadSafe//如果不存在,则通过客户端锁定实现Put
public class ListHelper<E> {
public List<E> list = Collections.synchronizedList(new ArrayList<E>());
public boolean putIfAbsent(E x){
synchronized(list){
boolean absent = !list.contains(x);
if(absent)
list.add(x);
return absent;
}
}
}
公共类ListHelper{
public List=Collections.synchronizedList(新的ArrayList());
公共布尔putIfAbsent(E x){
已同步(列表){
布尔缺席=!list.contains(x);
如果(缺席)
增加(x);
缺席返回;
}
}
}
快速前进,作者转到第三个案例-//使用组合实现put(如果不存在)
public class ImprovedList<E> implements List<E> {
private final List<E> list;
public ImprovedList(List<E> list){
this.list = list;
}
public synchronized boolean putIfAbsent(E x){
boolean contains = list.contains(x);
if(contains)
list.add(x);
return !contains;
}
}
public类ImprovedList实现列表{
私人最终名单;
公共改进列表(列表){
this.list=列表;
}
公共同步布尔putIfAbsent(E x){
布尔包含=list.contains(x);
如果(包含)
增加(x);
返回!包含;
}
}
即使
公共最终列表中的列表
,上述类的线程安全性如何
可能不是@ThreadSafe我相信这个例子来自JCIP。鉴于此,
在案例3中-它仍然是线程安全的,因为您通过公共同步布尔putIfAbsent(ex)
获得了对改进列表
对象的锁定。这里需要注意的关键点是即使这个类中所有其他使用list
的方法都使用synchronized
关键字。因此,在任何给定时间,只有一个线程能够使用这个list
对象
如果我们尝试添加其他与列表相关的方法,并了解其工作原理,就会更加清楚
public class ImprovedList<E> implements List<E> {
public final List<E> list;
public ImprovedList(List<E> list){
this.list = list;
}
public synchronized boolean putIfAbsent(E x){
boolean contains = list.contains(x);
if(contains)
list.add(x);
return !contains;
}
public synchronized void add(E x){
list.add(x);
}
//...Other list methods, but using synchronized keyword as above method.
}
因此,基本上总结是使用与list相同的锁(case-2)或使用显式锁(case-3)
关于@Markspace问题:
该列表是公开的,不是线程安全的。这是什么书 这是真的,在这方面,书中增加了一个警告。上面说, 如Collections.synchronizedList和其他集合 wrappers,ImprovedList假设一旦列表被传递给它的构造函数,客户端将不再直接使用基础列表,只通过ImprovedList访问它
你能告诉我为什么在第二种情况下,即使列表本身是同步的,也会有synchronized(list){…} 如果您完全使用其他线程安全的对象来创建某个对象O,这并不一定意味着O是线程安全的
public class ListHelper<E> {
private final List<E> list = new ArrayList<E>();
public boolean putIfAbsent(E x) {
synchronized (list) {
boolean absent = !list.contains(x);
if (absent) {
list.add(x);
}
return absent;
}
}
}
如果取出synchronized
语句,然后让两个线程同时使用相同的x
调用listHelper.putIfAbsent(x)
方法,会怎么样?两个线程都可以同时调用list.contains(x)
,并且都可以将缺席设置为true。一旦到达该点,它们都将调用list.add(x)
,线程安全列表将包含对同一对象的两个引用
这似乎不是你想要的
列表的线程安全性
意味着当这两个线程同时调用列表时,它们不会对列表的内部结构造成损害。添加(x)
。这意味着列表
将履行其承诺,即使多个线程同时使用它
list.contains(x)
方法实现了它的承诺:它两次都返回了false
,因为x
不在列表中。list.add(x)
实现了它的承诺,它将x
引用两次放入列表中,就像它被告知的那样
问题(如果取消同步)不在列表
对象中的任何位置,因为列表只包含一个x
引用的要求是您的要求
如果您不希望列表包含多个x
引用,那么您必须编写代码来防止它发生。这就是synchronized
语句所做的。它通过测试x
是否在列表中,然后对其进行处理,来进行原子操作。“原子”基本上意味着两个线程不能同时执行
第二种情况下的类是线程安全的,如JCIP中所示
“线程安全”一词没有确切的含义。我没有说ListHelper
不是线程安全的;我说过称之为“线程安全”是错误的
我只是采取了比布洛赫先生更保守的立场,仅此而已
如果您按照作者希望您使用的方式使用该类,则该类是线程安全的。但是,通过将列表
成员公开
,他还使您可以非常轻松地以非线程安全的方式使用他的类
我以写软件为生,在我的工作中,它对我来说既不必要,也不足以让程序工作。我的工作是制作让顾客满意的节目。如果有一种错误的方式来使用我的程序,如果我让错误的方式比正确的方式更吸引人,那么客户不会高兴,我的老板也不会高兴,而借口“是的,但它有效”也不会让我摆脱困境。简短回答。你需要再读一本书。这一条既令人困惑又不正确
//在错误的锁上同步,因此@NotThreadSafe
public class ListHelper<E> {
public List<E> list = Collections.synchronizedList(new ArrayList<E>());
public synchronized boolean putIfAbsent(E x){
boolean absent = !list.contains(x);
if(absent)
list.add(x);
return absent;
}
}
我假设书中说这是在错误对象上进行的同步
(不在“锁”上同步)的原因是因为列表
是一个public class ListHelper {
public List<String> list = Collections.synchronizedList(new ArrayList<String>());
public synchronized boolean putIfAbsent(String x) throws InterruptedException{// lock is ListHelper
boolean absent = !list.contains(x);
Thread.sleep(1000);
if(absent)
list.add(x);
return absent;
}
public void addList(String str) throws InterruptedException{
Thread.sleep(500);
list.add(str); // lock is SynchronizedCollection mutex
}
public static void main(String[] args) throws InterruptedException {
ListHelper lh = new ListHelper();
MyThread t1 = new MyThread(lh);
MyThread2 t2 = new MyThread2(lh);
t1.start();
t2.start();
Thread.sleep(1500);
System.out.println("size="+lh.list.size());
for(String str : lh.list){
System.out.println("item="+str);
}
}
class MyThread extends Thread{
ListHelper lh;
public MyThread(ListHelper lh){
this.lh=lh;
}
@Override
public void run() {
try {
lh.putIfAbsent("maksim");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
class MyThread2 extends Thread{
ListHelper lh;
public MyThread2(ListHelper lh){
this.lh=lh;
}
@Override
public void run() {
try {
lh.addList("maksim");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}