【问题标题】:static Value Sharing between two Threads Java两个线程Java之间的静态值共享
【发布时间】:2019-03-29 16:00:04
【问题描述】:

我有两个不同的子进程在两个不同的线程中运行两个不同的命令。第二个线程更新静态变量中的值,第一个线程获取并使用该值。

流程应该是这样的:线程2更新静态变量,线程1从静态变量中获取值,然后打印出来。 但是发生的流程是 thread1 首先从静态变量中获取值。在这种情况下,它有一个 null 值,然后 thread2 更新该值。

两个线程并行运行,我正在使用 ExecutorService 类来执行此操作。 我正在使用 Runtime 类来运行命令,并使用 while 循环在两个线程上连续读取 Stream 的输出。

线程 1 不断给出 (X,Y) 值,线程 2 仅在获得文本时才给出值。

我得到的输出:

(12, 123) null --> thread2 没有得到任何值所以它不会更新,thread1 将从静态变量中得到 null
(123,334) null --> thread1 从静态变量中获取值并使用它,然后 thread2 将值“Hello”更新为静态变量
(134,654) "Hello" --> 线程 1 拾取 "Hello" 并使用它,然后线程 2 将值 "World" 更新为静态变量

预期输出:

(12, 123) null --> thread2 没有得到任何值所以它不会更新,thread1 将从静态变量中得到 null
(123,334) "Hello" --> thread2 将值 "Hello" 更新为静态变量,thread1 拾取并使用它
(134,654) "World" --> thread2 将值 "World" 更新为静态变量,thread1 将其拾取并使用它

我也使用了 volatile 变量,但输出没有改变。我在这里错过了什么?请帮忙...

【问题讨论】:

标签: java multithreading variables static runtime


【解决方案1】:

您可以使用 java.util.concurrent.Semaphore(从 Java 5 开始)来控制同时访问它们共有的变量的线程数。您使用线程数(调用了 acquire() ) 允许使用它们的共同点,如果你传递 0,则意味着它们都不能修改它。

public class Threaad extends Thread{
volatile static String string;
public static void main(String[] args) {
    Semaphore semaphore = new Semaphore(0);
    Thread writer = new Write(semaphore);
    writer.setName("writer");
    Thread reader = new Read(semaphore);
    reader.setName("reader");
    ExecutorService service = Executors.newCachedThreadPool();
    service.execute(writer);
    service.execute(reader);
    service.shutdown();
}
}
class Write extends Thread{
    private Semaphore semaphore;
    Write(Semaphore semaphore){
        this.semaphore = semaphore;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            Threaad.string = String.valueOf(i);
            semaphore.release();
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class Read extends Thread{
    private Semaphore semaphore;
    Read(Semaphore semaphore){
        this.semaphore = semaphore;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                semaphore.acquire();
                System.out.println(Threaad.string);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

【讨论】:

  • 感谢您的回复,我已经尝试实现它,但问题仍然存在。 Thread1 从变量中获取值,然后打印它。由于 Thread2 仅在获得值时才放入该值,并且 Thread1 继续尝试获取它,因此 thread1 不打印任何内容..
  • 你必须更精确。我无法清楚地理解你想要什么。向我们展示您的代码,我会提供帮助。
  • 好的,我想要的很简单。有两种方法在后台同时运行两个不同的命令。假设 M1 和 M2。当用户点击设备时,M1 不断给你坐标 (X,Y)。只有当用户在设备上输入一些文本时,M2 才会打印。因此,当 M2 获取文本时,它会将文本放入静态变量中,并且 M1 会不断跟踪变量,如果它得到一个空值,那么它会假设用户没有输入任何文本,但如果变量有一些文本,这意味着用户输入了一些文本,然后 M1 从静态变量中选择文本并继续...
  • 也许您打印到输出的问题是因为“System.out.println()”方法的缓冲优化。请改用“System.err.println()”。
【解决方案2】:

使用线程时不要做的事情:在没有适当同步的情况下写入和读取相同的变量。您违反了此规则,因此会遇到未定义结果的预期问题。

想象一下下面的执行流程:

T1: set var to "Hello"
T2: checks var and notices it is a String, proceeds to print it.
T1: set var to null
T2: prints var. (which is already null again)

一种可能的解决方案是 T2 首先将值复制到线程局部变量,然后继续处理该变量,即:

while (running) {
  final String localString = staticString;
  if (localString != null) {
    System.out.println(localString);
  }
}

通过这种方式,您可以创建变量当前值的“快照”,并实际输出当时快照的内容。然而,这仍然不是线程之间同步的有效方式,因为快照状态是完全随机的。想象一下发生了以下情况:

T1: set var to "hello"
T1: set var to null
T2: checks var, it is null, do nothing.
T1: set var to "world"
T1: set var to null
T2: checks var, it is null, do nothing.

使用线程时,您需要记住,任何线程的状态在任何时候都是未定义的,除非您强制执行它。

有一些简单的方法可以强制执行特定状态,即synchronized 基本上的意思是“暂停所有工作,以便我可以完成我的工作”,但是这非常低效,因为如果你不断地暂停所有工作,那么并行工作毫无意义。

处理线程的更好方法是从架构的角度来看,即通过使用触发器。在您的情况下,编写器线程可能有一个 queue 仅包含相关信息(要输出的字符串),并且生产者线程会将字符串放入该队列:

T2: checks queue, it is empty, do nothing.
T1: t1.put("Hello")
T2: checks queue, it contains "Hello", print that.
T2: checks queue, it is empty, do nothing.
T2: checks queue, it is empty, do nothing.
T1: t1.put("World")
T2: checks queue, it contains "World", print that.

我建议通读 concurrent package 以了解 Java 提供的工具。几乎所有的问题都可以使用它们轻松解决。

【讨论】:

    猜你喜欢
    • 2015-06-17
    • 2013-06-20
    • 2010-11-23
    • 2011-06-23
    • 1970-01-01
    • 1970-01-01
    • 2015-09-08
    • 1970-01-01
    相关资源
    最近更新 更多