我知道
当你同步一段代码时,你指定哪个对象的锁 你想用作锁,所以你可以,例如,使用一些 第三方对象作为这段代码的锁。这给了你 能够拥有多个锁以进行代码同步 单个对象。
但是,我不明白需要将参数传递给块。因为我是否传递 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());
}
}
}
}
因为我是否传递 String 的实例、某个随机类的实例到同步块并不重要,因为无论传递给块的参数如何,同步块都能完美地工作。
该参数的目的有两个:
例如:
public int 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
的
both访问。当另一个线程正在调用
addValue
时,我们不能让某些东西调用 getSum
并踩在列表上。
this
(或更通常是 this
的某个字段)上进行同步,或者如果它是静态资源,则在类(或更通常情况下是某个类字段)上进行同步。同样,如果您只需要保护this
的特定字段,则无需同步。例如:
// (In MyClass)
public int 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 int 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
实例,因为字符串文字会自动放入字符串intern
池中。 (这意味着您过度同步;它是 JVM 范围的,而不是特定于实例的。)所以它可以工作,但并不是因为它只是任何对象。如果您将其更改为:
String str = "asd";
到
Object o = new Object();
并同步,它不会对帐户的访问进行序列化。
在您的示例中,同步的正确内容是
this.account
。
无论如何,同步块都会阻止两个线程同时进入临界区。那为什么需要传递参数呢?
同步块根据您传递给它的对象决定停止哪些线程。您传递的对象用作同步块保护的监视器部分的标识符。
您的程序中可能有许多监视器部分,所有这些部分都可以同时执行。例如,如果您有两个必须同时访问的不相关集合,则可以为每个集合设置单独的监视器部分。这样,只有当其他线程已经访问同一个集合时,线程才会停止;访问两个不同集合的两个不同线程将被允许同时进行。
你的第一个例子并不简单。它起作用的原因是字符串对象被初始化为字符串文字。由于文字的驻留,所有进入该函数的线程都将获得相同的
String
对象,因此同步块将正确保护监视器部分。
此外,您可能需要将实例 X 的对象传递给同步的参数,以防您需要将其传递到等待队列(通过使用 X.wait())。然后,您可以从另一个线程通过调用 X 上的 notify() 来通知该对象(只要需要)。