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

sbdsn5lh  于 2021-06-27  发布在  Java
关注(0)|答案(7)|浏览(533)

我想在多个线程之间共享一个变量,如下所示:

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

我想和大家分享 flag 在主线程和帮助线程之间,这是我创建的两个不同的java类。有什么办法吗?谢谢!

iszxjhcz

iszxjhcz1#

您可以使用锁定变量“a”和“b”并同步它们,以便以相反的顺序锁定“关键部分”。例如,通知“a”,然后锁定“b”,“打印”,通知“b”,然后锁定“a”。
请参考以下代码:-

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

        }
    }
}

}
输出:-1 a 2 b 3 a 4 b 5 a 6 b 7 a 8 b 9 a 10 b

fkvaft9z

fkvaft9z2#

使用 static 对你的案子没有帮助。
使用 synchronize 当一个变量被另一个线程使用时锁定它。
你应该使用 volatile 关键字保持变量在所有线程之间更新。
使用volatile是使类线程安全的另一种方法(如同步的原子 Package 器)。线程安全意味着一个方法或类示例可以被多个线程同时使用而不会出现任何问题。

kx7yvsdv

kx7yvsdv3#

两者 T1 以及 T2 可以引用包含此变量的类。
你可以让这个变量变为易变的,这意味着
对该变量的更改在两个线程中立即可见。
有关更多信息,请参阅本文。
volatile变量共享synchronized的可见性特性,但没有原子性特性。这意味着线程将自动看到volatile变量的最新值。它们可以用来提供线程安全性,但只能在非常有限的情况下使用:那些不在多个变量之间或变量的当前值和未来值之间施加约束的情况。
并注意使用 volatile 与更复杂的共享状态的方法相比。

jbose2ul

jbose2ul4#

使其在 T1 以及 T2 可以使这两个类包含对包含变量的对象的引用。
如果要在线程运行时修改变量,则需要考虑同步。最佳方法取决于您的具体要求,但主要选项如下:
使变量 volatile ;
把它变成一个 AtomicBoolean ;
对使用它的代码使用全面的同步。

14ifxucb

14ifxucb5#

原子布尔

npe简洁的回答总结了你的三个选择。我将为下面列出的第二项添加一些示例代码: AtomicBoolean .
你可以想到 AtomicBoolean 类作为在 boolean 价值观。
如果你示例化 AtomicBoolean 只需一次,就不必担心java内存模型中的可见性问题 volatile 作为解决方案(另一个答案中的第一项)。另外,您不必关心同步(另一个答案中的第三项),因为 AtomicBoolean 执行保护多线程访问其内部布尔值的功能。
让我们看一些示例代码。
首先,在现代java中,我们通常不处理 Thread 直接上课。我们现在有了executors框架来简化线程的处理。
下面的代码使用的是ProjectLoom技术,是java的未来版本。现在提供了初步的构建,构建在早期的accessjava16之上。这使得编码更加简单,并且 ExecutorService 存在 AutoCloseable 为了方便使用try-with-resources语法。但织布机项目与这个答案的要点无关;它只会使代码更简单,更容易理解为“结构化并发”。
这里的想法是我们有三个线程:原始线程,加上 ExecutorService 这将创建另外两个线程。两个新线程都报告了 AtomicBoolean . 第一个新线程立即执行此操作,而另一个线程在报告前等待10秒。同时,我们的主线程休眠5秒,唤醒,更改 AtomicBoolean 对象所包含的值,然后等待第二个线程唤醒并完成其工作,此时该线程上的报告已更改 AtomicBoolean 包含的值。当我们在每个事件之间设置秒数时,这仅仅是为了戏剧性的演示。真正的问题是,这些线程可能碰巧尝试访问 AtomicBoolean 同时,但该对象将以线程安全的方式保护对其内部布尔值的访问。防止同时访问是 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() );

方法,例如 AtomicBoolean#get 以及 AtomicBoolean#set 被构建为线程安全的,以在内部保护对嵌套的布尔值的访问。阅读其他各种方法。
运行时:

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

专业提示:在java中使用线程代码时,一定要学习brian goetz等人的优秀著作《java并发实践》。

eaf3rand

eaf3rand6#

除了其他建议之外,您还可以在控件类中 Package 该标志,并在父类中生成该标志的最终示例:

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

oyt4ldly7#

将其设置为静态可以解决此问题。
引用其他线程中的主线程并使该变量可见

相关问题