Java.Multithreading.How to share data between two threads?

Ways to Share Data Between Threads in Java

Let’s go step by step from simple to advanced.


🔸 1. Shared Object Reference (Most Common Way)

You create a shared object and pass it to multiple threads.

class Counter {
    int count = 0;
}

Counter sharedCounter = new Counter();

Thread t1 = new Thread(() -> {
    sharedCounter.count++;
});

Thread t2 = new Thread(() -> {
    sharedCounter.count++;
});

Problem: This is not thread-safe. Use synchronized, AtomicInteger, or Lock to fix it.

🔒 How to Make It Safe

✅ Option 1: synchronized block

class Counter {
    int count = 0;

    synchronized void increment() {
        count++;
    }
}

✅ Option 2: AtomicInteger

import java.util.concurrent.atomic.AtomicInteger;

class Counter {
    AtomicInteger count = new AtomicInteger();

    void increment() {
        count.incrementAndGet();
    }
}

🔸 2. Using BlockingQueue

Perfect for producer-consumer problems.

BlockingQueue<String> queue = new LinkedBlockingQueue<>();

Thread producer = new Thread(() -> {
    try {
        queue.put("Message from producer");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});

Thread consumer = new Thread(() -> {
    try {
        String message = queue.take();
        System.out.println("Consumer received: " + message);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});

✅ Thread-safe and blocking — no need for manual locking.

🔸 3. Using volatile for visibility

Use volatile when one thread updates a shared flag or value, and others just read it.

volatile boolean isRunning = true;

Ensures changes are immediately visible to all threads, but does not guarantee atomicity for compound actions (like count++).

🧠 Summary Table

MethodThread-safe?Best for
Shared object reference❌ (use sync)Simple shared state
synchronizedCritical sections, low-level control
AtomicInteger, etc.Lightweight atomic counters
BlockingQueueProducer-consumer, task queues
volatile✅ (for reads/writes)Flags, one-thread-writes scenarios

Java does provide a kind of pipe between threads — and it’s literally called a pipe, through the PipedInputStream / PipedOutputStream and PipedReader / PipedWriter classes!

Let’s explore this idea 💡


🧵🔗 Pipes Between Threads in Java

✅ What is it?

A pipe is a communication channel:
One thread writes data → the other reads it.

It mimics a Unix pipe or producer-consumer model.


📦 Java Pipe Classes:

For Binary DataFor Character Data
PipedInputStreamPipedReader
PipedOutputStreamPipedWriter

✅ Example: PipedOutputStream and PipedInputStream

import java.io.*;

public class PipeDemo {
    public static void main(String[] args) throws IOException {
        PipedOutputStream out = new PipedOutputStream();
        PipedInputStream in = new PipedInputStream(out);

        Thread writer = new Thread(() -> {
            try {
                out.write("Hello from writer!".getBytes());
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        Thread reader = new Thread(() -> {
            try {
                int data;
                while ((data = in.read()) != -1) {
                    System.out.print((char) data);
                }
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        writer.start();
        reader.start();
    }
}

🧠 Output:

Hello from writer!

⚠️ Caveats

  • Pipes are blocking: reading blocks until data is available.
  • Only for communication between two threads — not suitable for multi-thread broadcast or high throughput.
  • Not as robust or flexible as BlockingQueue.

✅ When to Use

  • When you need stream-like communication between two threads.
  • For custom producer-consumer logic where you want control over data format (bytes/characters).
This entry was posted in Без рубрики. Bookmark the permalink.