我的一本教科书提到 synchronized() 的论点必须是这样的……我知道这是错误的。但我听说由于 synchronized(this) 更安全,因此应该始终使用它。真的吗 ?谢谢:)
8 回答
不,它不必总是这样。在静态方法的情况下也根本不可能,因为没有 this。
此外,有时认为与此同步是错误的,因为此时锁定对象在外部可见。
public class Example {
private final Object lock = new Object();
// does not compile, there is no 'this' in static context.
public static void staticMethod() {
synchronized (this) {
}
}
public void method() {
int x = 3;
//there is risk that someone else outside our code
//uses same lock
synchronized (this) {
}
//this lock is private
synchronized (lock) {
}
}
}
我的一本教科书提到 synchronized() 的论点必须是这样的……我知道这是错误的。
这是不正确的。要么教科书不正确,要么你误解了它。Java 语言允许您在任何(非空)对象引用上进行同步。
但我听说由于 synchronized(this) 更安全,因此应该始终使用它。真的吗?
不,这也不是真的。它并不安全,您当然不应该总是锁定this
.
事实上,如果您正在编写一个需要锁定“自身”的通用库类,通常最好声明一个私有锁定字段;例如
private final Object myLock = new Object();
...并锁定该对象而不是this
. 这消除了可能发生的问题,即某些外部代码出于某种原因决定锁定库对象,从而导致不必要的争用,以及库类方法和外部代码之间可能出现的死锁。
我怀疑教科书试图提出的观点是,所有使用原始锁在数据结构上提供互斥和同步的方法都必须使用正确的对象作为锁。这不一定是数据结构对象本身,但它确实需要表示该对象......在某种意义上。(如果您不锁定表示数据结构的对象,则可能有一个线程在使用/更新数据结构时不排除其他线程。)
这是私有锁旨在避免的问题的草图。
/** This class implements thread-safe getting / setting by synchronizing on 'this' */
public class IntHolder {
private int value;
public int getValue() {
synchronized(this) { return value; }
}
public void setValue(int value)
synchronized(this) { this.value = value; }
}
/* Somewhere else, some other code (the "external code") used a holder instance
as the lock for some larger-scale synchronization. */
IntHolder h = ...
synchronized (h) {
/* Do something that takes a long time ... */
}
问题是,当外部代码保持锁定时h
,其他线程将无法读取或更改持有者的值。如果这是有意的……那很好。但是,如果该IntHolder
类型的线程安全旨在“只是一个实现细节”,那么您现在可能会遇到意外的失败情况。
如果您不确定 synchronized(this) 在该特定位置是否是一个好主意,IMO 最好在字段和方法上使用“volatile”和“synchronized”。
简而言之, volatile 将一个内在互斥锁放在一个变量上;synchronized 在函数调用上放置一个内在互斥锁;同步块在由参数锁定的那段代码周围放置一个内在互斥锁。
通常你会锁定一个对象,但有时你想锁定实例本身......不过,如果你只需要访问其中一个成员,我建议谨慎锁定整个类实例/fields(一开始你可以使用 synchronized 和 volatile 解决的问题)
您可能想要(或不想)锁定它的真正原因是锁定的范围,如果您锁定它,您可以看到锁。如果你初始化一个私有对象,锁就会被隐藏起来。
synchronized 的参数只是将要放置在哪个对象上。这取决于您要在同步块中执行的操作,这将确定参数应该是什么。使用this
会保留当前对象。
作为个人轶事,在我目前正在进行的项目中,我所有的同步保持都在 COM 端口上,因此发送和接收数据包之间没有冲突
您使用哪种锁并不重要。每个 java.lang.Object 都可以充当锁。您必须确保在相同可变状态下工作的所有操作都使用相同的锁。
所以synchronized(this)
完全一样安全
private final Object lock = new Object();
synchronized(lock)
如果您将非静态方法声明为synchronized
:
public synchronized void doSomething()
{
...
}
然后就同步了this
。因此,如果您的目标是让synchronized
块与上述同步的非静态方法同步,那么您确实需要使用synchronized(this)
.
但你是对的:你也可以写synchronized(someOtherObject)
,只要你知道这将锁定同步方法 ofsomeOtherObject
而不是 of this
。
(顺便说一下,对于静态方法,在表示包含类的实例synchronized
上同步。)Class
不,这不是你应该使用this
的。即使你对任何其他不是实例的对象应用锁this
。
synchronized(obj1){
-------
}
synchronized(obj2){
-------
}
在单个方法中,您可以编写类似上面的内容,首先获取某个对象的锁obj1
,然后执行工作并释放,然后获取 obj2 上的锁。
啊,臭名昭著的避免 Java 中的同步(this)?.
其实没有区别
public synchronized void doThis() {
}
和
public void doThis() {
synchronized (this) {
}
}
除了字节码级别。两者都解决线程安全问题。它们都会带来问题,因为如果(例如,您在其同步块中设置同一类的锁)可能会引入死锁。
如果您担心死锁,那么lock
应该使用专用的,如下所示:
public class MyClass {
private final Object lock = new Object(); //Must be final
public void doThis() {
synchronized (lock) {
}
}
}
或者,使用 Javajava.util.concurrent.Lock
接口和java.util.concurrent.locks.ReentrantLock
实现来做基本上锁..