有没有办法在两个完全独立的线程之间进行通信?
Is there a way to communicate between two completely independent threads?
我正在尝试在 Java 中不共享任何对象的两个线程之间进行通信(我正在构建一个 Java 代理)。我需要的是如下内容:
Thread agentattachThread = ...;
otherThread.send("Hello");
这将像代理附加线程中的中断一样处理,如下所示:
public void msgHandler(String msg) {
...
}
Thread.onMessageReceived(msgHandler);
EDIT - 考虑到我原来的 post 的答案,我的问题变成了:默认情况下,JVM 是否提供所有线程都可以引用的同步对象到?
我找到了一些选择。
- NIO 管道。
- 套接字。
- 全局同步对象。
- 信号。
我认为信号和回调机制是你所需要的。你必须自己实现它或使用第三方工具。
线程之间“通信”的唯一方法无需共享某些对象是通过Thread.interrupt()
Obviously, the only signal you can send by this means is a true
flag, and the receiver will only notice it when it uses certain methods, like Thread.interrupted()
,或引发InterruptedException
的方法。
由于这是如此有限,标准做法是在提供应用程序所需接口的线程之间共享线程安全对象。例如,您可以为每个线程提供对相同 BlockingQueue
的引用,并以此方式发送消息。
任何public静态字段都是全局可见的,因此任何线程都可以通过这样的字段访问诸如队列之类的共享对象。
想到的与 JVM 的“内置”最接近的是系统属性;本质上,这是一个共享映射,任何线程都可以通过它设置或获取 String
类型的条目。可能还有其他一些设施可能会以类似的方式被滥用,但我想不出有什么理由在静态字段服务时使用这样的东西。
您在标题中提问:
Is there a way to communicate between two completely independent threads?
(a) JVM 中的线程永远不会“完全独立”,因为线程共享 JVM 的资源和约束。
(b) 是的,object 可以通过多种方式在线程内或跨线程进行通信。
您在评论中提问:
is there an instance of such an object, created at startup by the JVM and to which all running threads could easily grab a reference to ?
线程不获取对 objects 的引用,线程中的 objects 执行方法获取引用。
JVM 启动时有许多 object 可用。例如,System.out
。在任何线程中执行的任何 object 都可以访问此类 objects.
Frameworks 提供额外的 objects 以在线程之间共享。例如,Jakarta Servlet 框架提供了一个 ServletContext
的实例,表示用于所有线程处理请求的 Web 应用程序。
但您需要的解决方案可能非常简单:传递所需的 object。 当实例化 Runnable
或 Callable
object要在线程内执行,将需要的资源传递给构造函数。与其考虑“获取”全局 object,不如考虑“被递交”需要 objects.
在此示例中,我们创建了 Runnable
class Counter
、Runnable task1
和 Runnable task2
的两个实例。对于每个实例,我们传递一个共享资源,一个 AtomicInteger
实例。 Atomic…
意味着 object 在内部是 thread-safe,因此我们可以安全地跨线程操作单个实例。
package work.basil.threading;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class App {
public static void main ( String[] args ) {
App app = new App();
app.demo();
}
private void demo () {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool( 3 );
AtomicInteger count = new AtomicInteger( 0 ); // Shared object.
Runnable task1 = new Counter( count ); // ⟸ Pass shared object to constructor of each `Runnable`/`Callable`.
Runnable task2 = new Counter( count );
scheduledExecutorService.scheduleAtFixedRate( task1 , 0 , 5 , TimeUnit.SECONDS );
scheduledExecutorService.scheduleAtFixedRate( task2 , 0 , 7 , TimeUnit.SECONDS );
try { Thread.sleep( Duration.ofSeconds( 40 ).toMillis() ); } catch ( InterruptedException e ) { e.printStackTrace(); }
scheduledExecutorService.shutdown();
try { scheduledExecutorService.awaitTermination( 5 , TimeUnit.SECONDS ); } catch ( InterruptedException e ) { e.printStackTrace(); }
System.out.println( "count = " + count );
}
class Counter implements Runnable {
AtomicInteger countingTracker;
public Counter ( AtomicInteger countingTracker ) {
this.countingTracker = countingTracker;
}
@Override
public void run () {
Instant instant = Instant.now();
int currentCount = this.countingTracker.incrementAndGet();
System.out.println( "`Counter` object in thread " + Thread.currentThread().getId() + " set this.countingTracker to: " + currentCount + " at " + instant + "." );
}
}
}
System.out
object 不一定按时间顺序报告。如您所见,第二个事件先于第一个事件出现在控制台上。因此,请关注 Instant
时刻以查看事件的真实顺序。
`Counter` object in thread 16 set this.countingTracker to: 2 at 2021-08-06T16:34:02.460073Z.
`Counter` object in thread 15 set this.countingTracker to: 1 at 2021-08-06T16:34:02.459625Z.
`Counter` object in thread 15 set this.countingTracker to: 3 at 2021-08-06T16:34:07.463863Z.
`Counter` object in thread 16 set this.countingTracker to: 4 at 2021-08-06T16:34:09.463869Z.
`Counter` object in thread 17 set this.countingTracker to: 5 at 2021-08-06T16:34:12.459813Z.
`Counter` object in thread 15 set this.countingTracker to: 6 at 2021-08-06T16:34:16.460429Z.
`Counter` object in thread 16 set this.countingTracker to: 7 at 2021-08-06T16:34:17.463699Z.
`Counter` object in thread 16 set this.countingTracker to: 8 at 2021-08-06T16:34:22.463406Z.
`Counter` object in thread 17 set this.countingTracker to: 9 at 2021-08-06T16:34:23.463713Z.
`Counter` object in thread 15 set this.countingTracker to: 10 at 2021-08-06T16:34:27.463391Z.
`Counter` object in thread 16 set this.countingTracker to: 11 at 2021-08-06T16:34:30.463637Z.
`Counter` object in thread 17 set this.countingTracker to: 12 at 2021-08-06T16:34:32.463385Z.
`Counter` object in thread 17 set this.countingTracker to: 13 at 2021-08-06T16:34:37.463273Z.
`Counter` object in thread 15 set this.countingTracker to: 14 at 2021-08-06T16:34:37.463329Z.
`Counter` object in thread 17 set this.countingTracker to: 15 at 2021-08-06T16:34:42.463201Z.
count = 15
你问过:
I am trying to communicate between two threads
一个解决方案是用thread-safe消息queue替换Runnable
object跨线程共享的AtomicInteger
objectobject,如Answer by erickson.
所述
我正在尝试在 Java 中不共享任何对象的两个线程之间进行通信(我正在构建一个 Java 代理)。我需要的是如下内容:
Thread agentattachThread = ...;
otherThread.send("Hello");
这将像代理附加线程中的中断一样处理,如下所示:
public void msgHandler(String msg) {
...
}
Thread.onMessageReceived(msgHandler);
EDIT - 考虑到我原来的 post 的答案,我的问题变成了:默认情况下,JVM 是否提供所有线程都可以引用的同步对象到?
我找到了一些选择。
- NIO 管道。
- 套接字。
- 全局同步对象。
- 信号。
我认为信号和回调机制是你所需要的。你必须自己实现它或使用第三方工具。
线程之间“通信”的唯一方法无需共享某些对象是通过Thread.interrupt()
Obviously, the only signal you can send by this means is a true
flag, and the receiver will only notice it when it uses certain methods, like Thread.interrupted()
,或引发InterruptedException
的方法。
由于这是如此有限,标准做法是在提供应用程序所需接口的线程之间共享线程安全对象。例如,您可以为每个线程提供对相同 BlockingQueue
的引用,并以此方式发送消息。
任何public静态字段都是全局可见的,因此任何线程都可以通过这样的字段访问诸如队列之类的共享对象。
想到的与 JVM 的“内置”最接近的是系统属性;本质上,这是一个共享映射,任何线程都可以通过它设置或获取 String
类型的条目。可能还有其他一些设施可能会以类似的方式被滥用,但我想不出有什么理由在静态字段服务时使用这样的东西。
您在标题中提问:
Is there a way to communicate between two completely independent threads?
(a) JVM 中的线程永远不会“完全独立”,因为线程共享 JVM 的资源和约束。
(b) 是的,object 可以通过多种方式在线程内或跨线程进行通信。
您在评论中提问:
is there an instance of such an object, created at startup by the JVM and to which all running threads could easily grab a reference to ?
线程不获取对 objects 的引用,线程中的 objects 执行方法获取引用。
JVM 启动时有许多 object 可用。例如,System.out
。在任何线程中执行的任何 object 都可以访问此类 objects.
Frameworks 提供额外的 objects 以在线程之间共享。例如,Jakarta Servlet 框架提供了一个 ServletContext
的实例,表示用于所有线程处理请求的 Web 应用程序。
但您需要的解决方案可能非常简单:传递所需的 object。 当实例化 Runnable
或 Callable
object要在线程内执行,将需要的资源传递给构造函数。与其考虑“获取”全局 object,不如考虑“被递交”需要 objects.
在此示例中,我们创建了 Runnable
class Counter
、Runnable task1
和 Runnable task2
的两个实例。对于每个实例,我们传递一个共享资源,一个 AtomicInteger
实例。 Atomic…
意味着 object 在内部是 thread-safe,因此我们可以安全地跨线程操作单个实例。
package work.basil.threading;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class App {
public static void main ( String[] args ) {
App app = new App();
app.demo();
}
private void demo () {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool( 3 );
AtomicInteger count = new AtomicInteger( 0 ); // Shared object.
Runnable task1 = new Counter( count ); // ⟸ Pass shared object to constructor of each `Runnable`/`Callable`.
Runnable task2 = new Counter( count );
scheduledExecutorService.scheduleAtFixedRate( task1 , 0 , 5 , TimeUnit.SECONDS );
scheduledExecutorService.scheduleAtFixedRate( task2 , 0 , 7 , TimeUnit.SECONDS );
try { Thread.sleep( Duration.ofSeconds( 40 ).toMillis() ); } catch ( InterruptedException e ) { e.printStackTrace(); }
scheduledExecutorService.shutdown();
try { scheduledExecutorService.awaitTermination( 5 , TimeUnit.SECONDS ); } catch ( InterruptedException e ) { e.printStackTrace(); }
System.out.println( "count = " + count );
}
class Counter implements Runnable {
AtomicInteger countingTracker;
public Counter ( AtomicInteger countingTracker ) {
this.countingTracker = countingTracker;
}
@Override
public void run () {
Instant instant = Instant.now();
int currentCount = this.countingTracker.incrementAndGet();
System.out.println( "`Counter` object in thread " + Thread.currentThread().getId() + " set this.countingTracker to: " + currentCount + " at " + instant + "." );
}
}
}
System.out
object 不一定按时间顺序报告。如您所见,第二个事件先于第一个事件出现在控制台上。因此,请关注 Instant
时刻以查看事件的真实顺序。
`Counter` object in thread 16 set this.countingTracker to: 2 at 2021-08-06T16:34:02.460073Z.
`Counter` object in thread 15 set this.countingTracker to: 1 at 2021-08-06T16:34:02.459625Z.
`Counter` object in thread 15 set this.countingTracker to: 3 at 2021-08-06T16:34:07.463863Z.
`Counter` object in thread 16 set this.countingTracker to: 4 at 2021-08-06T16:34:09.463869Z.
`Counter` object in thread 17 set this.countingTracker to: 5 at 2021-08-06T16:34:12.459813Z.
`Counter` object in thread 15 set this.countingTracker to: 6 at 2021-08-06T16:34:16.460429Z.
`Counter` object in thread 16 set this.countingTracker to: 7 at 2021-08-06T16:34:17.463699Z.
`Counter` object in thread 16 set this.countingTracker to: 8 at 2021-08-06T16:34:22.463406Z.
`Counter` object in thread 17 set this.countingTracker to: 9 at 2021-08-06T16:34:23.463713Z.
`Counter` object in thread 15 set this.countingTracker to: 10 at 2021-08-06T16:34:27.463391Z.
`Counter` object in thread 16 set this.countingTracker to: 11 at 2021-08-06T16:34:30.463637Z.
`Counter` object in thread 17 set this.countingTracker to: 12 at 2021-08-06T16:34:32.463385Z.
`Counter` object in thread 17 set this.countingTracker to: 13 at 2021-08-06T16:34:37.463273Z.
`Counter` object in thread 15 set this.countingTracker to: 14 at 2021-08-06T16:34:37.463329Z.
`Counter` object in thread 17 set this.countingTracker to: 15 at 2021-08-06T16:34:42.463201Z.
count = 15
你问过:
I am trying to communicate between two threads
一个解决方案是用thread-safe消息queue替换Runnable
object跨线程共享的AtomicInteger
objectobject,如Answer by erickson.