1

I am trying to communicate between two threads which do not share any object in Java (I am building a Java agent). What I need is something like the following :

Thread agentattachThread = ...;
otherThread.send("Hello");

And this would be handled like an interrupt in the Agent Attach thread, like so :

public void msgHandler(String msg) {
    ...
}

Thread.onMessageReceived(msgHandler);

EDIT - Considering the answers to my original post, my question becomes : does the JVM offer by default a synchronized object to which all threads could get a reference to ?

AntoineG
  • 93
  • 7
  • 1
    The only way to communicate between threads is by using synchronized objects – Saatvik Ramani Aug 06 '21 at 14:38
  • And those are either shared within java or you leverage e.g. the file system as a synchronization point, e.g. pipes. – luk2302 Aug 06 '21 at 14:50
  • Of course there are alot of ways. But which is the best you should decide. – KUTAY ZORLU Aug 06 '21 at 14:52
  • Does such an object exist by default ? Like is there an instance of a SynchronizedQueue which all threads could get a reference to ? – AntoineG Aug 06 '21 at 14:52
  • @AntoineG Please see [this](https://stackoverflow.com/questions/15460793/is-there-a-synchronized-queue-in-java) – aksappy Aug 06 '21 at 14:58
  • Sorry, I misformulated my question. Let me reformulate : 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 ? – AntoineG Aug 06 '21 at 15:22
  • 1
    @AntoineG Clarify your Question by editing the Question, not posting Comments. – Basil Bourque Aug 06 '21 at 15:55
  • Is defining your own static object in some class under your control (e.g. one of the classes used in the implementation of the agent) to synchronize on not an option? – nanofarad Aug 06 '21 at 16:00
  • @SaatvikRamani You can also use concurrent collections which don't use synchronization. – Mark Rotteveel Aug 06 '21 at 16:02
  • @nanofarad this I can do, and that is where I am heading at the moment – AntoineG Aug 06 '21 at 16:18

3 Answers3

6

The only way to "communicate" from thread to thread without sharing some object is via 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(), or a method that raises InterruptedException.

Because this is so limited, it's standard practice to share a thread-safe object between threads which offers the interface needed by the application. For example, you could give each thread a reference to the same BlockingQueue and send messages that way.

Any public, static field is globally visible, so a shared object such as a queue could be accessed via such a field by any thread.

The closest thing to that "built-in" to the JVM that comes to mind are system properties; essentially, this is a shared map via which any thread can set or get String-type entries. There might be some other facilities that could be abused in a similar way, but I can't imagine any reason to use something like this when a static field would serve.

erickson
  • 265,237
  • 58
  • 395
  • 493
  • Thinking aloud: The static field works, as long as you can rely on a class being loaded by only a single class loader. With even the simplest class loader isolation scenario, this would no longer work VM-wide, because you would have one static field per loaded class. But other than in this specific case, the suggested solution is great. – kriegaex Aug 07 '21 at 02:58
1

There are some choices I found.

  1. NIO pipe.
  2. socket.
  3. a global synchronized object.
  4. signal.

I think signal and callback machanism are what you need. You have to implement it by yourself or use thirdparty tool.

Ander
  • 66
  • 6
  • I finally used a synchronized object, declared as a public static attribute of an already defined class – AntoineG Aug 07 '21 at 08:45
1

You asked in your title:

Is there a way to communicate between two completely independent threads?

(a) Threads within a JVM are never “completely independent”, in that threads share the resources and constraints of the JVM.

(b) Yes, there are many ways for objects to communicate, both within a thread or across threads.

You asked in your comment:

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 ?

Threads don't grab references to objects, the objects executing methods within a thread grab references.

There are many objects available when a JVM launches. For example, System.out. Any object executing in any thread can access such objects.

Frameworks provide additional objects to be shared across threads. For example, the Jakarta Servlet framework provides an instance of ServletContext representing the web app for use across all the threads processing requests.

But probably the solution you need is quite simple: Pass the objects needed. When instantiating the Runnable or Callable object to be executed within a thread, pass needed resources to the constructor. Instead of thinking in terms of "grabbing" a global object, think of "being handed" needed objects.

In this example, we create two instances of our Runnable class Counter, Runnable task1 and Runnable task2. For each instance, we pass a shared resource, an AtomicInteger instance. The Atomic… means the object is internally thread-safe, so we can safely manipulate the single instance across threads.

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 + "." );
        }
    }
}

The System.out object does not necessarily report chronologically. As you can see here, the second event appeared on the console before the first. So follow the Instant moments to see true order of events.

`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

You asked:

I am trying to communicate between two threads

One solution is to replace the AtomicInteger object shared by Runnable objects across threads with a thread-safe message queue object, as mentioned in Answer by erickson.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • Thank you very much ! I ended up declaring a public static attribute in a class and using it for inter-thread communication, I don't know why I hadn't looked at the problem this way – AntoineG Aug 07 '21 at 08:44