java 将参数传递给同步块的目的是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29876641/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is the purpose of passing parameter to synchronized block?
提问by John Rambo
I know that
我知道
When you synchronize a block of code, you specify which object's lock you want to use as the lock, so you could, for example, use some third-party object as the lock for this piece of code. That gives you the ability to have more than one lock for code synchronization within a single object.
当您同步一段代码时,您指定要使用哪个对象的锁作为锁,例如,您可以使用某个第三方对象作为这段代码的锁。这使您能够在单个对象中拥有多个用于代码同步的锁。
However, I don't understand the need of passing argument to the block. Because it doesn't matter whether I pass String's instance, Some random class's instance to the synchronized block as the synchronized block works perfectly irrespective of the parameter being passed to the block.
但是,我不明白需要将参数传递给块。因为我是否传递 String 的实例无关紧要,一些随机类的实例传递给同步块,因为同步块可以完美地工作,而不管传递给块的参数如何。
So my question is if anyways synchronized block stops two threads from entering the critical section simultaneously. Then why there is a need of passing an argument. (I mean acquire lock on some random object by default).
所以我的问题是,无论如何同步块是否会阻止两个线程同时进入临界区。那么为什么需要传递参数。(我的意思是默认获取一些随机对象的锁)。
I hope I framed my question correctly.
我希望我正确地提出了我的问题。
I have tried the following example with random parameters being to the synchronized block.
我已经尝试了以下示例,其中随机参数是同步块。
public class Launcher {
public static void main(String[] args) {
AccountOperations accOps=new AccountOperations();
Thread lucy=new Thread(accOps,"Lucy");
Thread sam=new Thread(accOps,"Sam");
lucy.start();
sam.start();
}
}
Using non-static synchronized block:
使用非静态同步块:
public class AccountOperations implements Runnable{
private Account account = new Account();
public void run(){
for(int i=0;i<5;i++){
makeWithdrawal(10);
}
}
public void makeWithdrawal(int amount){
String str="asd"
synchronized (str /* pass any non-null object the synchronized block works*/) {
if(account.getAmount()>10){
try{
Thread.sleep(5000);
}catch(InterruptedException e){
e.printStackTrace();
}
account.withdraw(amount);
System.out.println(Thread.currentThread().getName()+" has withdrawn 10, current balance "+ account.getAmount());
}else{
System.out.println("Insufficient funds "+account.getAmount());
}
}
}
}
Using static synchronized block:
使用静态同步块:
public class AccountOperations implements Runnable{
private static Account account = new Account();
public void run(){
for(int i=0;i<5;i++){
makeWithdrawal(10);
}
}
public static void makeWithdrawal(int amount){
synchronized (String.class /* pass any class literal synchronized block works*/) {
if(account.getAmount()>10){
try{
Thread.sleep(5000);
}catch(InterruptedException e){
e.printStackTrace();
}
account.withdraw(amount);
System.out.println(Thread.currentThread().getName()+" has withdrawn 10, current balance "+ account.getAmount());
}else{
System.out.println("Insufficient funds "+account.getAmount());
}
}
}
}
采纳答案by dasblinkenlight
if anyways synchronized block stops two threads from entering the critical section simultaneously. Then why there is a need of passing an argument?
如果无论如何同步块会阻止两个线程同时进入临界区。那为什么需要传递参数呢?
Synchronized block decides which threads to stop based on the object that you pass to it. The object that you pass serves as the identifier of the critical section guarded by the synchronized block.
同步块根据您传递给它的对象决定停止哪些线程。您传递的对象用作由同步块保护的临界区的标识符。
You may have many critical sections in your program, all of which could be executed concurrently with each other. For example, if you have two unrelated collections that must be accessed concurrently, you can set up separate critical sections for each collection. This way threads would be stopped only when other threads are already accessing the same collection; two different threads accessing two different collections would be allowed to proceed concurrently.
您的程序中可能有许多临界区,所有临界区都可以同时执行。例如,如果您有两个必须同时访问的不相关集合,您可以为每个集合设置单独的临界区。这种方式只有在其他线程已经访问同一个集合时才会停止线程;允许访问两个不同集合的两个不同线程同时进行。
Your first example is non-trivial. The reason it works is that the string object is initialized to a string literal. Due to literal's interning, all threads entering the function will obtain the same String
object, so the synchronized block will properly guard the critical section.
你的第一个例子是不平凡的。它起作用的原因是字符串对象被初始化为字符串文字。由于literal的interning,所有进入函数的线程都会获得同一个String
对象,所以synchronized块会适当地保护临界区。
回答by T.J. Crowder
Because it doesn't matter whether I pass String's instance, Some random class's instance to the synchronized block as the synchronized block works perfectly irrespective of the parameter being passed to the block.
因为我是否传递 String 的实例无关紧要,一些随机类的实例传递给同步块,因为同步块可以完美地工作,而不管传递给块的参数如何。
The purpose of the parameter is twofold:
参数的目的有两个:
It makes it possible to synchronize otherblocks on the same object, so that if you have two blocks of code that may change the state of the same object, they don't interfere with each other.
For example:
public void getSum() { int sum = 0; synchronized (this.list) { for (Thingy t : this.list) { sum += t.getValue(); } } return sum; } public void addValue(int value) { synchronized (this.list) { this.list.add(new Thingy(value)); } }
There, it's important that we synchronize bothaccesses to
list
across threads. We can't have something callingaddValue
and stomping on the list while another thread is callinggetSum
.It makes it possible to ensure you're synchronizing with the correct granularity. If you're serializing access to an instance-specific resource, then it doesn't make sense to do that across instances; you should allow multiple threads into the block provided they're operating on different instances. That's why you would synchronize on
this
(or more usually some field ofthis
) for an instance-specific resource, or the class (or more usually some class field) if it were a static resource. Similarly, there's no need to synchronize onthis
if you only need to protect a specific field of it.For example:
// (In MyClass) public void getThingySum() { int sum = 0; synchronized (this.thingyList) { for (Thingy t : this.thingyList) { sum += t.getValue(); } } return sum; } public void addThingy(Thingy t) { synchronized (this.thingyList) { this.thingyList.add(t); } } public void getNiftySum() { int sum = 0; synchronized (this.niftyList) { for (Nifty n : this.niftyList) { sum += n.getValue(); } } return sum; } public void addNifty(Nifty n) { synchronized (this.niftyList) { this.niftyList.add(t); } }
There, we synchronize access to
this.thingyList
onthis.thingyList
, notthis
orMyClass.class
. It's fine if one thread is callinggetThingySum
while another thread callsaddNifty
, so synchronizing onthis
would be overkill.
这使得同步同一对象上的其他块成为可能,这样如果您有两个可能改变同一对象状态的代码块,它们就不会相互干扰。
例如:
public void getSum() { int sum = 0; synchronized (this.list) { for (Thingy t : this.list) { sum += t.getValue(); } } return sum; } public void addValue(int value) { synchronized (this.list) { this.list.add(new Thingy(value)); } }
在那里,我们同步是很重要的两个访问
list
跨线程。我们不能有东西叫addValue
和跺脚就行了,而另一个线程正在调用getSum
。它可以确保您以正确的粒度进行同步。如果您正在序列化对特定于实例的资源的访问,那么跨实例这样做是没有意义的;您应该允许多个线程进入块,前提是它们在不同的实例上运行。这就是为什么您要为特定于实例的资源或类(或更通常是某个类字段)同步
this
(或更常见的某些字段this
),如果它是静态资源。同样,this
如果您只需要保护它的特定字段,则无需同步。例如:
// (In MyClass) public void getThingySum() { int sum = 0; synchronized (this.thingyList) { for (Thingy t : this.thingyList) { sum += t.getValue(); } } return sum; } public void addThingy(Thingy t) { synchronized (this.thingyList) { this.thingyList.add(t); } } public void getNiftySum() { int sum = 0; synchronized (this.niftyList) { for (Nifty n : this.niftyList) { sum += n.getValue(); } } return sum; } public void addNifty(Nifty n) { synchronized (this.niftyList) { this.niftyList.add(t); } }
在那里,我们同步访问
this.thingyList
onthis.thingyList
、 notthis
或MyClass.class
。如果一个线程正在调用getThingySum
而另一个线程正在调用,那很好addNifty
,因此同步this
将是过度的。
Re your str
example:
重新你的str
例子:
public void makeWithdrawal(int amount){
String str="asd"
synchronized (str /* pass any non-null object the synchronized block works*/) {
if(account.getAmount()>10){
try{
Thread.sleep(5000);
}catch(InterruptedException e){
e.printStackTrace();
}
account.withdraw(amount);
System.out.println(Thread.currentThread().getName()+" has withdrawn 10, current balance "+ account.getAmount());
}else{
System.out.println("Insufficient funds "+account.getAmount());
}
}
}
The comment there is incorrect, any non-null
instance will notadequately protect that code. The reason the above seems to work is string interning: The same String
instance is used by all threads, because string literals are automatically put in the string intern
pool. (Which means you're over-synchronizing; it's JVM-wide, not instance-specific.) So it works, but not because it's just any object. If you changed it from:
那里的注释不正确,任何非null
实例都不能充分保护该代码。上面似乎工作的原因是字符串实习:String
所有线程都使用同一个实例,因为字符串文字会自动放入字符串intern
池中。(这意味着您过度同步;它是 JVM 范围的,而不是特定于实例的。)所以它可以工作,但不是因为它只是任何对象。如果您将其更改为:
String str = "asd";
to
到
Object o = new Object();
and synchronized on that, it would do nothing to serialize access to the account.
并同步于此,它不会对帐户的序列化访问做任何事情。
In your example, the correct thing to synchronize on is this.account
.
在您的示例中,同步的正确内容是this.account
.