当前位置: 首页 > 知识库问答 >
问题:

将参数传递给同步块的目的是什么?

乜元魁
2023-03-14

我知道

当您同步一个代码块时,您可以指定要使用哪个对象的锁作为锁,这样,例如,您可以使用某个第三方对象作为这段代码的锁。这使您能够在单个对象中拥有多个用于代码同步的锁。

但是,我不明白是否需要将参数传递给块。因为我是否传递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());
            }
        }

    }

}

共有3个答案

华聪
2023-03-14

此外,如果需要将实例X的对象传递给等待队列(使用X.wait()),则可能需要将其传递给synchronized的参数。然后,从另一个线程,您可以通过在X上调用notify()来通知对象(只要需要)。

怀展
2023-03-14

无论如何,synchronized块阻止两个线程同时进入临界区。那为什么需要传递一个论点呢?

同步块根据传递给它的对象决定要停止的线程。您传递的对象用作由同步块保护的监视器部分的标识符。

程序中可能有许多监视部分,所有这些部分都可以同时执行。例如,如果有两个必须同时访问的不相关集合,则可以为每个集合设置单独的监视部分。这样,仅当其他线程已经访问同一集合时,线程才会停止;将允许访问两个不同集合的两个不同线程同时进行。

你的第一个例子很重要。它工作的原因是字符串对象被初始化为字符串文字。由于literal的interning,所有进入该函数的线程都将获得相同的< code>String对象,因此synchronized块将适当地保护monitor部分。

景帅
2023-03-14

因为我是否将 String 的实例传递给同步块并不重要,所以无论传递给块的参数如何,同步块都可以完美地工作。

参数的目的有两个:

>

  • 它使同步同一对象上的其他块成为可能,这样,如果有两块代码可能会更改同一对象的状态,它们就不会相互干扰。

    例如:

    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));
        }
    }
    

    在这里,跨线程同步对列表的两次访问非常重要。当另一个线程正在调用getSum时,我们不能让一些东西调用addValue,然后在列表上跺脚。

    它可以确保您以正确的粒度进行同步。如果您正在序列化对特定于实例的资源的访问,那么跨实例这样做是没有意义的;如果多线程运行在不同的实例上,你应该允许它们进入这个块。这就是为什么您要在特定于实例的资源的< code>this(或者更常见的是< code>this的某个字段)上同步,或者在类(或者更常见的是某个类字段)是静态资源的情况下同步。同样,如果您只需要保护它的一个特定字段,就没有必要在< code>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的访问,而不是thisMyClass.class。如果一个线程调用getThingySum,而另一个线程调用addNift,那么在this上同步将是多余的。

    Re您的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实例,因为字符串文字会自动放入stringintern池中。(这意味着您过度同步;它是JVM范围的,而不是特定于实例的。)所以它有效,但并不是因为它只是任何对象。如果您将其更改为:

    String str = "asd";
    

    Object o = new Object();
    

    并且同步,它不会对序列化对帐户的访问做任何事情。

    在您的示例中,正确的同步方式是< code>this.account。

  •  类似资料:
    • 问题内容: 我知道 同步代码块时,可以指定要将哪个对象的锁用作锁,例如,可以将某些第三方对象用作此代码段的锁。这样一来,您就可以在一个对象中拥有多个锁来进行代码同步。 但是,我不明白需要将参数传递给该块。因为我是否传递String的实例都没有关系,所以某些随机类的实例作为同步块可以完美地工作,而与传递给该块的参数无关。 所以我的问题是 ,无论如何,同步块是否阻止两个线程同时进入关键部分。 那为什么

    • 我有一个简单的 Servlet 应用程序的安全部分,我需要将参数传递给我的应用程序的安全部分。 流量: 第三方需要使用我的登录机制(简单的安全servlet) 用户尝试转到: mycompany.com/loginApp/login?pref=1 “/login”url是安全的,因此应用程序服务器告诉浏览器重定向到我的登录名.jsp,但此时URL更新为:mycompany.com/loginApp

    • 问题内容: 我正在使用Go内置的http服务器,并拍拍来响应一些URL: 我需要向该处理函数传递一个额外的参数-一个接口。 如何向处理程序函数发送额外的参数? 问题答案: 通过使用闭包,您应该能够做您想做的事情。 更改为以下内容(未测试): 然后对

    • 我在解一个有很多常数的非线性方程 我创建了一个用于解决以下问题的函数: 然后我想做: 但是正在解包并向函数传递太多参数,因此我得到: TypeError:terminalV()正好接受2个参数(给定6个) 那么,我的问题是,我是否可以通过某种方式将元组传递给调用的函数?

    • 是否可以使用UI将项目数组传递到swagger块中的单个参数中?我在ruby\rails应用程序中使用它。请求类型是GET。 我有一个接受许多参数的搜索 API。所有这些都是单一值,但一个设施除外。它接受一个项目数组,并且可以使用类似于下面显示的 URL 进行调用(下面的示例显示了传递的两个值 1 和 2)。 我的参数是这样设置的: 当我在swagger中传递数组作为参数时,EG[1]会生成以下U

    • 问题内容: 在我的RCP应用程序中,左侧有一个用于导航的视图,右侧有一个用于视图的文件夹。透视图看起来像这样: 我想根据用户在导航树中选择的内容打开不同的视图。认为这并不难。我的导航树视图: 这似乎很好。仅有一个小问题:我需要以某种方式将对象(例如,selectedItem)传递给我的视图,以使用户与其内容进行交互。我怎么做? 我看到了一些示例,其中一些同事编写了自己的视图,并将其放置在右侧。然后