[英]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. T1
和T2
都可以引用包含此变量的类。
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.要使其在
T1
和T2
的实例之间可见,您可以使这两个类包含对包含变量的对象的引用。
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:
最佳方法取决于您的确切要求,但主要选项如下:
volatile
;volatile
;AtomicBoolean
;AtomicBoolean
;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.这使得编码更简单,
ExecutorService
是AutoCloseable
以便于与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#get
和AtomicBoolean#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.