Java同步方法锁定对象或方法?
如果我在同一个类中有 2 个同步方法,但每个方法访问不同的变量,那么 2 个线程可以同时访问这 2 个方法吗?锁定发生在对象上,还是与同步方法内的变量一样具体?
示例:
class X {
private int a;
private int b;
public synchronized void addA(){
a++;
}
public synchronized void addB(){
b++;
}
}
2 个线程可以访问类 X 的同一个实例,同时执行 x.addA(
) 和 x.addB()
吗?
If I have 2 synchronized methods in the same class, but each accessing different variables, can 2 threads access those 2 methods at the same time? Does the lock occur on the object, or does it get as specific as the variables inside the synchronized method?
Example:
class X {
private int a;
private int b;
public synchronized void addA(){
a++;
}
public synchronized void addB(){
b++;
}
}
Can 2 threads access the same instance of class X performing x.addA(
) and x.addB()
at the same time?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
如果您将该方法声明为同步(正如您通过键入
public synchronized void addA()
所做的那样),您将在整个对象上进行同步,因此,从同一对象访问不同变量的两个线程无论如何都会互相阻塞。如果您只想一次只同步一个变量,这样两个线程在访问不同变量时就不会互相阻塞,那么您可以在
synchronized()
块中分别对它们进行同步。如果a
和b
是您将使用的对象引用:但由于它们是基元,您不能这样做。
我建议您使用 AtomicInteger 代替:
If you declare the method as synchronized (as you're doing by typing
public synchronized void addA()
) you synchronize on the whole object, so two threads accessing a different variable from the same object would block each other anyway.If you want to synchronize only on one variable at a time, so two threads won't block each other while accessing different variables, you have synchronize on them separately in
synchronized ()
blocks. Ifa
andb
were object references you would use:But since they're primitives you can't do this.
I would suggest you to use AtomicInteger instead:
Synchronized 在方法声明上是这样的语法糖:
在静态方法上,它是这样的语法糖:
我认为如果 Java 设计者知道现在对同步的理解,他们就不会添加语法糖,因为它更常见而不是导致并发的糟糕实现。
Synchronized on the method declaration is syntactical sugar for this:
On a static method it is syntactical sugar for this:
I think if the Java designers knew then what is understood now about synchronization, they would not have added the syntactical sugar, as it more often than not leads to bad implementations of concurrency.
来自“Java™ 教程”中的同步方法:
来自同步块的“Java™教程”:
(强调我的)
假设您有 2 个非交错变量。因此,您希望同时从不同的线程访问每一个。您需要不在对象类本身上定义锁,而是在Object类上定义锁,如下所示(来自第二个Oracle链接的示例):
From "The Java™ Tutorials" on synchronized methods:
From "The Java™ Tutorials" on synchronized blocks:
(Emphasis mine)
Suppose you have 2 non-interleaving variables. So you want to access to each one from a different threads at the same time. You need to define the lock not on the object class itself, but on the class Object like below (example from the second Oracle link):
访问的锁位于对象上,而不是方法上。在方法内访问哪些变量是无关紧要的。
在方法中添加“同步”意味着运行代码的线程必须先获取对象的锁,然后才能继续。添加“静态同步”意味着运行代码的线程必须获取类对象上的锁才能继续。或者,您可以将代码包装在如下所示的块中:
以便您可以指定必须获取其锁的对象。
您可以选择:
The lock accessed is on the object, not on the method. Which variables are accessed within the method is irrelevant.
Adding "synchronized" to the method means the thread running the code must acquire the lock on the object before proceeding. Adding "static synchronized" means the thread running the code must acquire the lock on the class object before proceeding. Alternatively you can wrap code in a block like this:
so that you can specify the object whose lock must be acquired.
If you want to avoid locking on the containing object you can choose between:
来自oracle文档链接
使方法同步有两个效果:
请查看此文档 页面了解内在锁和锁行为。
这将回答您的问题:在同一个对象 x 上,当其中一个同步方法执行正在进行时,您不能同时调用 x.addA() 和 x.addB() 。
From oracle documentation link
Making methods synchronized has two effects:
Have a look at this documentation page to understand intrinsic locks and lock behavior.
This will answer your question: On same object x , you can't call x.addA() and x.addB() at same time when one of the synchronized methods execution is in progress.
如果您有一些未同步的方法并且正在访问和更改实例变量。在您的示例中:
当其他线程处于同一对象的同步方法中并且可以更改实例变量时,任意数量的线程都可以同时访问这些非同步方法。
例如:-
您需要避免非同步方法访问实例变量并更改它的情况,否则没有必要使用同步方法。
在下面的场景中:-
只有一个线程可以进入 addA 或 addB 方法,但同时任意数量的线程可以进入 changeState 方法。没有两个线程可以同时进入 addA 和 addB (因为对象级锁定),但同时任意数量的线程可以进入changeState。
If you have some methods which are not synchronized and are accessing and changing the instance variables. In your example:
any number of threads can access these non synchronized methods at the same time when other thread is in the synchronized method of the same object and can make changes to instance variables.
For e.g :-
You need to avoid the scenario that non synchronized methods are accessing the instance variables and changing it otherwise there is no point of using synchronized methods.
In the below scenario:-
Only one of the threads can be either in addA or addB method but at the same time any number of threads can enter changeState method. No two threads can enter addA and addB at same time(because of Object level locking) but at same time any number of threads can enter changeState.
这个例子(虽然不是很好)可以让我们更深入地了解锁定机制。如果incrementA已同步,而incrementB未同步,则incrementB将为尽快执行,但如果 incrementB 也同步,那么它必须在 incrementB 之前“等待”incrementA 完成> 可以完成其工作。
这两种方法都被调用到单个实例 - 对象,在本例中它是:job,“竞争”线程是aThread和main。
尝试在 incrementB 中使用“同步”,如果不使用它,您将看到不同的结果。如果 incrementB 是“同步” ' 那么它也必须等待 incrementA() 完成。每个变体运行几次。
This example (although not pretty one) can provide more insight into locking mechanism. If incrementA is synchronized, and incrementB is not synchronized, then incrementB will be executed ASAP, but if incrementB is also synchronized then it has to 'wait' for incrementA to finish, before incrementB can do its job.
Both methods are called onto single instance - object, in this example it is: job, and 'competing' threads are aThread and main.
Try with 'synchronized' in incrementB and without it and you will see different results.If incrementB is 'synchronized' as well then it has to wait for incrementA() to finish. Run several times each variant.
您可以执行以下操作。在这种情况下,您使用 a 和 b 上的锁来同步,而不是“this”上的锁。我们不能使用 int,因为原始值没有锁,所以我们使用 Integer。
You can do something like the following. In this case you are using the lock on a and b to synchronized instead of the lock on "this". We cannot use int because primitive values don't have locks, so we use Integer.
是的,它会阻止其他方法,因为同步方法适用于所指出的整个类对象......但无论如何,它只会在执行以下操作时仅阻止其他线程执行sum 在它进入的任何方法 addA 或 addB 中,因为当它完成时......一个线程将释放对象,另一个线程将访问另一个方法,依此类推,完美工作。
我的意思是“同步”正是为了阻止另一个线程在特定代码执行中访问另一个线程。所以最后这段代码可以正常工作了。
最后一点,如果存在“a”和“b”变量,而不仅仅是唯一变量“a”或任何其他名称,则无需同步此方法,因为访问其他 var(其他内存)是完全安全的地点)。
也会发挥作用
Yes, it will block the other method because synchronized method applies to the WHOLE class object as pointed .... but anyway it will block the other thread execution ONLY while performing the sum in whatever method addA or addB it enters, because when it finish ... the one thread will FREE the object and the other thread will access the other method and so on perfectly working.
I mean the "synchronized" is made precisely for blocking the other thread from accessing another while in a specific code execution. SO FINALLY THIS CODE WILL WORK FINE.
As a final note, if there is an 'a' and 'b' variables, not just an unique variable 'a' or whatever other name, there is no need to synchronize this methods cause it is perfectly safe accesing other var (Other memory location).
Will work as well
在Java同步中,如果一个线程想要进入
synchronized
方法/块,它将获取以下锁:不仅在一个同步方法上,线程还使用
因此线程将调用同步方法
addA() 将获取
addA()
和addB()
上的锁,因为两者都是同步的。因此具有相同对象的其他线程无法执行addB()。
In Java synchronization,if a thread want to enter into
synchronized
method/block, it will acquire the lock on:not just on one synchronized method that thread is using
So the thread which will call the synchronized method
addA()
for example, will acquire a lock onaddA()
andaddB()
as both are synchronized.So the other threads with same object cannot execute
addB()
.这可能不起作用,因为从 Integer 到 int 的装箱和自动装箱(反之亦然)取决于 JVM,并且如果两个不同的数字在 -128 到 127 之间,则它们很可能会被散列到相同的地址。
This might not work as the boxing and autoboxing from Integer to int and viceversa is dependant on JVM and there is high possibility that two different numbers might get hashed to same address if they are between -128 and 127.