【问题标题】:How to update immediately shared variables between threads如何立即更新线程之间的共享变量
【发布时间】:2013-03-27 02:31:14
【问题描述】:

我的测试程序要求输入一个字符串并每 2 秒打印一次。我已经阅读了一些关于 java 内存模型或线程如何不立即更新主内存上的变量的内容。

我已尝试使用 volatilestatic 属性。同步修改了变量line 的代码块。使用wait()/notifi() 更改变量和其他一些变量。如何将line 分配为参考而不是值?我是否使用了我尝试错误的方法?为什么对象在充当监视器时可以保持完美同步,而在充当指针时却不能?

public class TestSharedVariable {
    static String line= "";

    public static void main(String[] args) {
        // For reading from keyboard
        Scanner keyboard = new Scanner(System.in);

        Printer p = new Printer(line);
        p.start();

        // Reads a line from keyboard into the shared variable
        while(!line.equals("quit")){
            line = keyboard.nextLine(); 

        }
    }
}

class Printer extends Thread{
    private volatile String line;

    public Printer(String palabra){
        this.line = palabra;
    }

    /*  Prints the line each 2 sec  */
    @Override
    public  void run(){
        while(!line.equals("quit")){
            try {
                sleep(2000);
            } catch (InterruptedException e) {e.printStackTrace();}
            System.out.println(this.getName() + ": " + line);
        }
    }
}

输出:

    Thread-0: 
    asdf
    Thread-0: 
    Thread-0: 

【问题讨论】:

    标签: java multithreading asynchronous concurrency


    【解决方案1】:

    你有两个主要问题:

    • 首先,字符串是不可变的,因此一旦创建,您就无法更改字符串变量的状态,例如组成它的字符,并且
    • 您将两个引用变量分配给同一个对象,但希望一个变量在另一个变量的分配发生更改时更改其分配,这不是 Java 引用变量的工作方式。

    换句话说,即使您最初将 Printer 对象的行变量设置为与 TestSharedVariable 的静态行变量相同的引用,但稍后更改 TestSharedVariable 的静态行变量的引用对打印机行变量的引用分配没有影响。这与线程无关,与理解引用变量有关。想想引用变量,如 C 或其他类似语言中的指针。如果两个变量指向同一个对象,将一个变量更改为指向不同的对象对第一个变量没有影响。

    要使您的代码正常工作,两个变量必须共享对同一个可变对象的引用,并且您必须将可变对象的 state 而不是 reference 更改为它。

    例如,

    import java.util.Scanner;
    
    public class TestSharedVariable {
       static volatile MutableObject mutableObject = new MutableObject();
    
       public static void main(String[] args) {
          // For reading from keyboard
          Scanner keyboard = new Scanner(System.in);
    
          Printer p = new Printer(mutableObject);
          new Thread(p, "Print thread").start();
    
          // Reads a line from keyboard into the shared variable
          while (!mutableObject.getData().equals("quit")) {
             mutableObject.setData(keyboard.nextLine());
    
          }
       }
    }
    
    class Printer implements Runnable {
       private volatile MutableObject mutableObject;
    
       public Printer(MutableObject mutableObject) {
          this.mutableObject = mutableObject;
       }
    
       /* Prints the line each 2 sec */
       @Override
       public void run() {
          while (!mutableObject.getData().equals("quit")) {
             try {
                Thread.sleep(2000);
             } catch (InterruptedException e) {
                e.printStackTrace();
             }
             Thread thread = Thread.currentThread();
             System.out.println(thread.getName() + ": " + mutableObject);
          }
       }
    }
    
    class MutableObject {
       private String data = "";
    
       public String getData() {
          return data;
       }
    
       public void setData(String data) {
          this.data = data;
       }
    
       @Override
       public String toString() {
          return data;
       }
    
    }
    

    【讨论】:

    • 知道了。感谢您的超快速回答。生病将我的变量包装成一个可变对象。一开始我认为int 不是可变的,但Integer 是可变的。快速搜索告诉我,JAVA 试图避免可变对象。
    • @demil133:请注意,您的代码可以使用原语,因此无需包装它们。 Java 很聪明地避免了字符串的可变对象或原始包装器对象,因为它可以为您省去很多麻烦。
    • int/boolean/char 等等?我已经尝试过了。但我以前错了。我会再试一次。
    • @demil133:不,我错了。正如我最初回答的那样,您编写它的方式需要一个 reference 变量的包装器。另请注意,实现 Runnables 比扩展 Thread 更好(请参阅上面对我的代码的更改以了解我的意思)。
    猜你喜欢
    • 1970-01-01
    • 2021-01-27
    • 2019-05-01
    • 2017-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多