将参数传递给同步块的目的是什么?
What is the purpose of passing parameter to synchronized block?
我知道
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.
但是,我不明白向块传递参数的必要性。因为我是否将 String 的实例传递给同步块并不重要,因为同步块可以完美地工作,而不管传递给块的参数如何。
所以我的问题是 如果同步块阻止两个线程同时进入临界区。那么为什么需要传递参数。 (我的意思是默认获取一些随机对象的锁)。
我希望我的问题是正确的。
我尝试了以下示例,其中随机参数是同步块。
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();
}
}
使用非静态同步块:
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());
}
}
}
}
使用静态同步块:
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());
}
}
}
}
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.
参数的用途有两个:
这使得在同一个对象上同步 other 块成为可能,这样如果你有两个代码块可能会改变同一个对象的状态对象,互不干扰
例如:
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
。当另一个线程正在调用 getSum
.
时,我们不能让某些东西调用 addValue
并踩在列表上
它可以确保您以正确的粒度进行同步。如果您正在序列化对特定于实例的资源的访问,那么跨实例执行此操作没有意义;你应该允许多个线程进入块,前提是它们在不同的实例上运行。这就是为什么您会在 this
(或更常见的 this
的某个字段)上同步特定于实例的资源,或者 class(或更常见的某些 class 字段)如果它是静态资源。同样,如果您只需要保护它的特定字段,则无需在 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
上同步对 this.thingyList
的访问,而不是 this
或 MyClass.class
。如果一个线程调用 getThingySum
而另一个线程调用 addNifty
没问题,因此在 this
上进行同步就太过分了。
关于你的 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());
}
}
}
那里的评论不正确,任何非null
实例都不会充分保护该代码。以上似乎有效的原因是 string interning:所有线程都使用相同的 String
实例,因为字符串文字会自动放入字符串 intern
池中. (这意味着您过度同步;它是 JVM 范围内的,而不是特定于实例的。)所以它有效,但不是因为它只是任何对象。如果您将其更改为:
String str = "asd";
到
Object o = new Object();
并在此基础上同步,它不会序列化对帐户的访问。
在您的示例中,正确的同步对象是 this.account
。
if anyways synchronized block stops two threads from entering the critical section simultaneously. Then why there is a need of passing an argument?
同步块根据您传递给它的对象决定停止哪些线程。您传递的对象用作同步块保护的监视器部分的标识符。
您的程序中可能有很多监控部分,所有监控部分都可以相互并发执行。例如,如果您有两个必须同时访问的不相关集合,则可以为每个集合设置单独的监视器部分。这样,只有当其他线程已经在访问同一个集合时,线程才会停止;将允许访问两个不同集合的两个不同线程并发进行。
你的第一个例子很重要。它起作用的原因是字符串对象被初始化为字符串文字。由于 literal 的驻留,所有进入函数的线程都将获得相同的 String
对象,因此 synchronized 块将适当地保护 monitor 部分。
此外,您可能需要将实例 X 的对象传递给同步的参数,以防需要将其传递给等待队列(通过使用 X.wait())。然后,您可以从另一个线程通过调用 X 上的 notify() 来通知对象(无论何时)。
我知道
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.
但是,我不明白向块传递参数的必要性。因为我是否将 String 的实例传递给同步块并不重要,因为同步块可以完美地工作,而不管传递给块的参数如何。
所以我的问题是 如果同步块阻止两个线程同时进入临界区。那么为什么需要传递参数。 (我的意思是默认获取一些随机对象的锁)。
我希望我的问题是正确的。
我尝试了以下示例,其中随机参数是同步块。
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();
}
}
使用非静态同步块:
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());
}
}
}
}
使用静态同步块:
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());
}
}
}
}
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.
参数的用途有两个:
这使得在同一个对象上同步 other 块成为可能,这样如果你有两个代码块可能会改变同一个对象的状态对象,互不干扰
例如:
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
。当另一个线程正在调用getSum
. 时,我们不能让某些东西调用 它可以确保您以正确的粒度进行同步。如果您正在序列化对特定于实例的资源的访问,那么跨实例执行此操作没有意义;你应该允许多个线程进入块,前提是它们在不同的实例上运行。这就是为什么您会在
this
(或更常见的this
的某个字段)上同步特定于实例的资源,或者 class(或更常见的某些 class 字段)如果它是静态资源。同样,如果您只需要保护它的特定字段,则无需在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
上同步对this.thingyList
的访问,而不是this
或MyClass.class
。如果一个线程调用getThingySum
而另一个线程调用addNifty
没问题,因此在this
上进行同步就太过分了。
addValue
并踩在列表上
关于你的 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());
}
}
}
那里的评论不正确,任何非null
实例都不会充分保护该代码。以上似乎有效的原因是 string interning:所有线程都使用相同的 String
实例,因为字符串文字会自动放入字符串 intern
池中. (这意味着您过度同步;它是 JVM 范围内的,而不是特定于实例的。)所以它有效,但不是因为它只是任何对象。如果您将其更改为:
String str = "asd";
到
Object o = new Object();
并在此基础上同步,它不会序列化对帐户的访问。
在您的示例中,正确的同步对象是 this.account
。
if anyways synchronized block stops two threads from entering the critical section simultaneously. Then why there is a need of passing an argument?
同步块根据您传递给它的对象决定停止哪些线程。您传递的对象用作同步块保护的监视器部分的标识符。
您的程序中可能有很多监控部分,所有监控部分都可以相互并发执行。例如,如果您有两个必须同时访问的不相关集合,则可以为每个集合设置单独的监视器部分。这样,只有当其他线程已经在访问同一个集合时,线程才会停止;将允许访问两个不同集合的两个不同线程并发进行。
你的第一个例子很重要。它起作用的原因是字符串对象被初始化为字符串文字。由于 literal 的驻留,所有进入函数的线程都将获得相同的 String
对象,因此 synchronized 块将适当地保护 monitor 部分。
此外,您可能需要将实例 X 的对象传递给同步的参数,以防需要将其传递给等待队列(通过使用 X.wait())。然后,您可以从另一个线程通过调用 X 上的 notify() 来通知对象(无论何时)。