我想在多个线程之间共享变量,如下所示:
boolean flag = true;
T1 main = new T1();
T2 help = new T2();
main.start();
help.start();
我想在main和help线程之间共享flag
,这些是我创建的两个不同的Java类。有办法做到这一点吗?谢谢!
答案 0 :(得分:27)
T1
和T2
都可以引用包含此变量的类
然后,您可以将此变量设置为 volatile ,这意味着
在两个线程中都可以立即看到对该变量的更改。
有关详细信息,请参阅this article。
易变变量分享同步的可见性功能但是 没有原子性特征。这意味着线程会 自动查看易变变量的最新值。 它们可用于提供线程安全性,但仅限于非常 限制案例集:不在其间施加约束的案例 多个变量或变量的当前值与其之间的变量 未来的价值观。
请注意使用volatile
与使用更复杂的共享状态的方式的优缺点。
答案 1 :(得分:13)
除了其他建议之外 - 您还可以将标志包装在控件类中,并在父类中创建它的最终实例:
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();
}
}
}
答案 2 :(得分:4)
答案 3 :(得分:3)
要使T1
和T2
的实例之间可见,您可以使这两个类包含对包含变量的对象的引用。
如果要在线程运行时修改变量,则需要考虑同步。最佳方法取决于您的确切要求,但主要选项如下:
volatile
; AtomicBoolean
; 答案 4 :(得分:3)
使用static
对您的情况没有帮助。
在另一个线程正在使用变量时,使用synchronize
会锁定该变量。
您应该使用volatile
关键字来使变量在所有线程之间保持更新。
使用volatile是使类线程安全的另一种方式(例如同步的原子包装)。线程安全意味着一个方法或类实例可以被多个线程同时使用而没有任何问题。
答案 5 :(得分:0)
您可以使用锁定变量" 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
答案 6 :(得分:0)
AtomicBoolean
简洁的 Answer by NPE 总结了您的三个选项。我将为此处列出的第二项添加一些示例代码:AtomicBoolean
。
您可以将 AtomicBoolean
类视为围绕 boolean
值提供一些线程安全包装。
如果您只将 AtomicBoolean
实例化一次,那么您无需担心需要 volatile
作为解决方案的 Java Memory Model 中的可见性问题(其他答案中的第一项) .此外,您不必担心同步(另一个答案中的第三项),因为 AtomicBoolean
执行保护多线程访问其内部布尔值的功能。
让我们看一些示例代码。
首先,在现代 Java 中,我们通常不直接处理 Thread
类。我们现在有了 Executors framework 来简化线程的处理。
下面的代码使用了 Project Loom 技术,即将用于 Java 的未来版本。初步 builds available now,建立在抢先体验 Java 16 之上。这使得编码更简单,ExecutorService
为 AutoCloseable
以方便与 try-with-resources 语法一起使用。但是 Project Loom 与这个答案的要点无关;它只是使代码更简单,更容易理解为 “structured concurrency”。
这里的想法是我们有三个线程:原始线程,加上一个 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 Concurrency in Practice。