简体   繁体   English

在多个不同线程之间共享一个变量

[英]Sharing a variable between multiple different threads

I want to share a variable between multiple threads like this:我想在多个线程之间共享一个变量,如下所示:

boolean flag = true;
T1 main = new T1();
T2 help = new T2();
main.start();
help.start();

I'd like to share flag between main and help thread where these are two different Java classes I've created.我想在主线程和帮助线程之间共享flag ,这是我创建的两个不同的 Java 类。 Is any way to do this?有没有办法做到这一点? Thanks!谢谢!

Both T1 and T2 can refer to a class containing this variable. T1T2都可以引用包含此变量的类。
You can then make this variable volatile , and this means that然后你可以使这个变量volatile ,这意味着
Changes to that variable are immediately visible in both threads.对该变量的更改在两个线程中立即可见。

See this article for more info.有关更多信息,请参阅这篇文章

Volatile variables share the visibility features of synchronized but none of the atomicity features.可变变量共享同步的可见性特征,但不共享原子性特征。 This means that threads will automatically see the most up-to-date value for volatile variables .这意味着线程将自动看到 volatile 变量的最新值 They can be used to provide thread safety, but only in a very restricted set of cases: those that do not impose constraints between multiple variables or between a variable's current value and its future values.它们可用于提供线程安全,但仅限于非常有限的情况:那些不在多个变量之间或在变量的当前值与其未来值之间强加约束的情况。

And note the pros/cons of using volatile vs more complex means of sharing state.并注意使用volatile与更复杂的状态共享方式的优缺点。

In addition to the other suggestions - you can also wrap the flag in a control class and make a final instance of it in your parent class:除了其他建议 - 您还可以将标志包装在控件类中并在父类中创建它的最终实例:

public class Test {
  class Control {
    public volatile boolean flag = false;
  }
  final Control control = new Control();

  class T1 implements Runnable {
    @Override
    public void run() {
      while ( !control.flag ) {

      }
    }
  }

  class T2 implements Runnable {
    @Override
    public void run() {
      while ( !control.flag ) {

      }
    }
  }

  private void test() {
    T1 main = new T1();
    T2 help = new T2();

    new Thread(main).start();
    new Thread(help).start();
  }

  public static void main(String[] args) throws InterruptedException {
    try {
      Test test = new Test();
      test.test();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

Using static will not help your case.使用static对您的情况没有帮助。

Using synchronize locks a variable when it is in use by another thread.当变量被另一个线程使用时,使用synchronize锁定一个变量。

You should use volatile keyword to keep the variable updated among all threads.您应该使用volatile关键字来保持变量在所有线程之间更新。

Using volatile is yet another way (like synchronized, atomic wrapper) of making class thread safe.使用 volatile 是使类线程安全的另一种方式(如同步、原子包装)。 Thread safe means that a method or class instance can be used by multiple threads at the same time without any problem.线程安全意味着一个方法或类实例可以被多个线程同时使用,没有任何问题。

To make it visible between the instances of T1 and T2 you could make the two classes contain a reference to an object that contains the variable.要使其在T1T2的实例之间可见,您可以使这两个类包含对包含变量的对象的引用。

If the variable is to be modified when the threads are running, you need to consider synchronization.如果要在线程运行时修改变量,则需要考虑同步。 The best approach depends on your exact requirements, but the main options are as follows:最佳方法取决于您的确切要求,但主要选项如下:

  • make the variable volatile ;使变量volatile
  • turn it into an AtomicBoolean ;把它变成一个AtomicBoolean
  • use full-blown synchronization around code that uses it.在使用它的代码周围使用全面的同步。
  1. Making it static could fix this issue.将其设为静态可以解决此问题。
  2. Reference to the main thread in other thread and making that variable visible引用其他线程中的主线程并使该变量可见

You can use lock variables "a" and "b" and synchronize them for locking the "critical section" in reverse order.您可以使用锁定变量“a”和“b”并同步它们以按相反的顺序锁定“临界区”。 Eg.例如。 Notify "a" then Lock "b" ,"PRINT", Notify "b" then Lock "a".通知“a”然后锁定“b”,“PRINT”,通知“b”然后锁定“a”。

Please refer the below the code :请参考以下代码:

public class EvenOdd {

    static int a = 0;

    public static void main(String[] args) {

        EvenOdd eo = new EvenOdd();

        A aobj = eo.new A();
        B bobj = eo.new B();

        aobj.a = Lock.lock1;
        aobj.b = Lock.lock2;

        bobj.a = Lock.lock2;
        bobj.b = Lock.lock1;

        Thread t1 = new Thread(aobj);
        Thread t2 = new Thread(bobj);

        t1.start();
        t2.start();
    }

    static class Lock {
        final static Object lock1 = new Object();
        final static Object lock2 = new Object();
    }

    class A implements Runnable {

        Object a;
        Object b;

        public void run() {
            while (EvenOdd.a < 10) {
                try {
                    System.out.println(++EvenOdd.a + " A ");
                    synchronized (a) {
                        a.notify();
                    }
                    synchronized (b) {
                        b.wait();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class B implements Runnable {

        Object a;
        Object b;

        public void run() {
            while (EvenOdd.a < 10) {

                try {
                    synchronized (b) {
                        b.wait();
                        System.out.println(++EvenOdd.a + " B ");
                    }
                    synchronized (a) {
                        a.notify();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

OUTPUT :输出 :

1 A 
2 B 
3 A 
4 B 
5 A 
6 B 
7 A 
8 B 
9 A 
10 B 

AtomicBoolean

The succinct Answer by NPE sums up your three options. NPE的简洁回答总结了您的三个选项。 I'll add some example code for the second item listed there: AtomicBoolean .我将为AtomicBoolean列出的第二项添加一些示例代码: AtomicBoolean

You can think of the AtomicBoolean class as providing some thread-safety wrapping around a boolean value.您可以将AtomicBoolean类视为提供一些围绕boolean值的线程安全包装。

If you instantiate the AtomicBoolean only once, then you need not worry about the visibility issue in the Java Memory Model that requires volatile as a solution (the first item in that other Answer).如果您只将AtomicBoolean实例化一次,那么您无需担心Java 内存模型中的可见性问题,该问题需要volatile作为解决方案(其他答案中的第一项)。 Also, you need not concern yourself with synchronization (the third item in that other Answer) because AtomicBoolean performs that function of protecting multi-threaded access to its internal boolean value.此外,您不必担心同步(另一个答案中的第三项),因为AtomicBoolean执行保护多线程访问其内部布尔值的功能。

Let's look at some example code.让我们看一些示例代码。

Firstly, in modern Java we generally do not address the Thread class directly.首先,在现代 Java 中,我们通常不直接处理Thread类。 We now have the Executors framework to simplify handling of threads.我们现在有了Executors 框架来简化线程的处理。

This code below is using Project Loom technology, coming to a future version of Java.下面的代码使用了Project Loom技术,即将用于 Java 的未来版本。 Preliminary builds available now , built on early-access Java 16 .初步构建现在可用,构建在早期访问的Java 16 上 This makes for simpler coding, with ExecutorService being AutoCloseable for convenient use with try-with-resources syntax.这使得编码更简单, ExecutorServiceAutoCloseable以便于与try-with-resources语法一起使用。 But Project Loom is not related to the point of this Answer;但是 Project Loom 与这个答案的要点无关; it just makes for simpler code that is easier to understand as “structured concurrency” .它只是使更简单的代码更容易理解为“结构化并发”

The idea here is that we have three threads: the original thread, plus a ExecutorService that will create two more threads.这里的想法是我们有三个线程:原始线程,加上一个将创建另外两个线程的ExecutorService The two new threads both report the value of our AtomicBoolean .这两个新线程都报告了AtomicBoolean的值。 The first new thread does so immediately, while the other waits 10 seconds before reporting.第一个新线程立即执行此操作,而另一个在报告前等待 10 秒。 Meanwhile, our main thread sleeps for 5 seconds, wakes, changes the AtomicBoolean object's contained value, and then waits for that second thread to wake and complete its work the report on the now-altered AtomicBoolean contained value.同时,我们的主线程休眠 5 秒,唤醒,更改AtomicBoolean对象的包含值,然后等待第二个线程唤醒并完成关于现在更改的AtomicBoolean包含值的报告。 While we are installing seconds between each event, this is merely for dramatic demonstration.虽然我们在每个事件之间设置秒数,但这只是为了戏剧性的演示。 The real point is that these threads could coincidently try to access the AtomicBoolean simultaneously, but that object will protect access to its internal boolean value in a thread-safe manner.真正的重点是这些线程可能同时尝试访问AtomicBoolean ,但该对象将以线程安全的方式保护对其内部布尔值的访问。 Protecting against simultaneous access is the job of the Atomic… classes.防止同时访问是Atomic…类的工作。

try (
        ExecutorService executorService = Executors.newVirtualThreadExecutor() ;
)
{
    AtomicBoolean flag = new AtomicBoolean( true );

    // This task, when run, will immediately report the flag.
    Runnable task1 = ( ) -> System.out.println( "First task reporting flag = " + flag.get() + ". " + Instant.now() );

    // This task, when run, will wait several seconds, then report the flag. Meanwhile, code below waits a shorter time before *changing* the flag.
    Runnable task2 = ( ) -> {
        try { Thread.sleep( Duration.ofSeconds( 10 ) ); } catch ( InterruptedException e ) { e.printStackTrace(); }
        System.out.println( "Second task reporting flag = " + flag.get() + ". " + Instant.now() );
    };

    executorService.submit( task1 );
    executorService.submit( task2 );

    // Wait for first task to complete, so sleep here briefly. But wake before the sleeping second task awakens.
    try { Thread.sleep( Duration.ofSeconds( 5 ) ); } catch ( InterruptedException e ) { e.printStackTrace(); }
    System.out.println( "INFO - Original thread waking up, and setting flag to false. " + Instant.now() );
    flag.set( false );
}
// At this point, with Project Loom technology, the flow-of-control blocks until the submitted tasks are done.
// Also, the `ExecutorService` is automatically closed/shutdown by this point, via try-with-resources syntax.
System.out.println( "INFO - Tasks on background threads are done. The `AtomicBoolean` and threads are gone." + Instant.now() );

Methods such as AtomicBoolean#get and AtomicBoolean#set are built to be thread-safe, to internally protect access to the boolean value nested within.诸如AtomicBoolean#getAtomicBoolean#set被构建为线程安全的,以在内部保护对嵌套在其中的布尔值的访问。 Read up on the various other methods as well.阅读其他各种方法。

When run:运行时:

First task reporting flag = true. 2021-01-05T06:42:17.367337Z
INFO - Original thread waking up, and setting flag to false. 2021-01-05T06:42:22.367456Z
Second task reporting flag = false. 2021-01-05T06:42:27.369782Z
INFO - Tasks on background threads are done. The `AtomicBoolean` and threads are gone.2021-01-05T06:42:27.372597Z

Pro Tip: When engaging in threaded code in Java, always study the excellent book, Java Concurrency in Practice by Brian Goetz et al.专业提示:在 Java 中使用线程代码时,请务必阅读 Brian Goetz 等人撰写的优秀书籍Java Concurrency in Practice

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM