我想了解Java在同步列表时的确切工作原理。 我们假设我有这个代码:
List list = Collections.synchronizedList(new ArrayList());
synchronized(list) {
Iterator i = list.iterator();
while (i.hasNext())
foo(i.next());
}
并且线程(线程1)正在执行它。
另一个线程(线程2):
list.add(....)
而前一个代码正在线程1中执行。
线程2会等到线程1完成该代码吗?同步就像"等待另一个完成"?
或者,例如,如果3个线程到达synchronized(list) {}
包裹的某些代码,它们是仅按顺序启动它,而不是同时启动它吗?
答案 0 :(得分:4)
线程2会等到线程1完成该代码吗?,即syncronzying就像“等待另一个完成”?
是如果某个线程获得了锁,那么在初始线程释放锁之前,没有其他线程可以获得相同的锁。
或者,例如,如果3个线程到达由synchronized(list){}包装的某些代码,它们是仅按顺序启动它,而不是同时启动它吗?
是,按顺序,但未指定订单。从您的角度来看,它可能是首先获得它的3个线程中的任何一个。
P.S。 Mutex获取(已同步)没有任何公平性。未指定哪个线程以及何时获取锁定。 例如:如果您拥有持有锁的线程A,并且还有其他3个线程正在等待相同的锁,并且线程A在一段时间后释放锁;那么 4个线程中的任何(包括线程A)都可以获取该锁
答案 1 :(得分:2)
如果查看Collections.synchronizedList
后面的源代码,您会看到它创建Collections.SynchronizedList
的实例,该实例使用设置为{{1的内部mutex
进行锁定当没有传递其他值时。
您的代码会锁定this
实例本身,因此在使用list
时会起作用,但必须手动同步Collections.synchronizedList(List)
才能避免使用List.iterator
。
答案 2 :(得分:1)
- 同步不在方法级别/代码级别。它处于对象级别。
- 如果要同步对象,则访问该对象的任何线程必须首先锁定对象的监视器。
- 如果thread1在监视器上有锁定,则尝试访问该对象的任何其他线程都必须等到thread1释放锁定。
醇>
答案 3 :(得分:1)
在您的示例中,线程2将等待,因为线程1已获取同步列表上的锁定。
一般来说,同步块中包含的代码完全在同一个Object上的“下一个”同步块之前执行。
答案 4 :(得分:1)
Java中的同步是合作的,而不是强制性的。我的意思是,您希望同步保护数据,但该语言只允许您同步操作数据的代码块。您有责任确保所有可以触及您数据的代码都是同步的。
(这是其他一些答案的含义,当他们说,"同步是代码级别,而不是对象级别。)
在您的示例中,您编写了一个操作列表的同步代码块。这将阻止两个或多个线程同时进入那个代码块,但它通常不会阻止其他线程从其他代码块中同时操作列表,即使一个线程在同步块。
另请注意:监视器对象(您同步的对象)不必是您尝试保护的数据的一部分。例如,您可以有两个列表;并且您可能希望保证两个列表之间的某种关系(例如,两者必须是相同的长度)。在那种情况下,您同步哪个列表?答案可能都不是。如,
private Object lock = new Object();
private List<Foo> fooList = ...;
private List<Bar> barList = ...;
... doSomethingFascinating(...) {
synchronized(lock) {
//Use your shameful imagination here.
}
}