Java Thread Communication Methods: Synchronized, Polling, wait/notify, and Piped Streams
This article explains four common Java thread communication techniques—synchronized blocks, while‑polling, wait/notify, and piped streams—providing code examples and discussing their advantages, drawbacks, and practical usage in multithreaded applications.
In daily development, multithreading and concurrency are not often used by some developers, but they are frequent interview topics; therefore this article collects typical Java thread communication methods for reference.
1. Introduction
This article summarizes the author's understanding of Java thread communication, using code examples extracted from books.
2. Thread Communication Methods
① Synchronized
Synchronization uses the synchronized keyword to achieve communication between threads that share the same object.
Example:
public class MyObject {
synchronized public void methodA() {
// do something....
}
synchronized public void methodB() {
// do some other thing
}
}
public class ThreadA extends Thread {
private MyObject object;
// constructor omitted
@Override
public void run() {
super.run();
object.methodA();
}
}
public class ThreadB extends Thread {
private MyObject object;
// constructor omitted
@Override
public void run() {
super.run();
object.methodB();
}
}
public class Run {
public static void main(String[] args) {
MyObject object = new MyObject();
// Thread A and Thread B hold the same object
ThreadA a = new ThreadA(object);
ThreadB b = new ThreadB(object);
a.start();
b.start();
}
}Because Thread A and Thread B hold the same MyObject instance, Thread B must wait for Thread A to finish methodA() before it can execute methodB() , thus achieving communication via shared memory.
② While Polling
Code example:
import java.util.ArrayList;
import java.util.List;
public class MyList {
private List
list = new ArrayList
();
public void add() {
list.add("elements");
}
public int size() {
return list.size();
}
}
public class ThreadA extends Thread {
private MyList list;
public ThreadA(MyList list) {
super();
this.list = list;
}
@Override
public void run() {
try {
for (int i = 0; i < 10; i++) {
list.add();
System.out.println("Added " + (i + 1) + " elements");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadB extends Thread {
private MyList list;
public ThreadB(MyList list) {
super();
this.list = list;
}
@Override
public void run() {
try {
while (true) {
if (list.size() == 5) {
System.out.println("==5, thread B will exit");
throw new InterruptedException();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Test {
public static void main(String[] args) {
MyList service = new MyList();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
}
}Thread A continuously changes a condition, while Thread B repeatedly checks the condition ( list.size()==5 ) in a while loop, achieving communication but wasting CPU resources because the polling thread does no useful work.
Additionally, visibility problems can arise due to memory visibility; if Thread B reads a locally cached variable, it may not see updates made by Thread A, potentially causing a dead loop.
③ wait/notify Mechanism
Code example:
import java.util.ArrayList;
import java.util.List;
public class MyList {
private static List
list = new ArrayList
();
public static void add() {
list.add("anyString");
}
public static int size() {
return list.size();
}
}
public class ThreadA extends Thread {
private Object lock;
public ThreadA(Object lock) {
super();
this.lock = lock;
}
@Override
public void run() {
try {
synchronized (lock) {
if (MyList.size() != 5) {
System.out.println("wait begin " + System.currentTimeMillis());
lock.wait();
System.out.println("wait end " + System.currentTimeMillis());
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadB extends Thread {
private Object lock;
public ThreadB(Object lock) {
super();
this.lock = lock;
}
@Override
public void run() {
try {
synchronized (lock) {
for (int i = 0; i < 10; i++) {
MyList.add();
if (MyList.size() == 5) {
lock.notify();
System.out.println("Notification sent");
}
System.out.println("Added " + (i + 1) + " elements!");
Thread.sleep(1000);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Run {
public static void main(String[] args) {
try {
Object lock = new Object();
ThreadA a = new ThreadA(lock);
a.start();
Thread.sleep(50);
ThreadB b = new ThreadB(lock);
b.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}Thread A calls wait() when the condition ( list.size()!=5 ) is not met, releasing the CPU and entering a blocked state; Thread B adds elements and calls notify() when the condition becomes true, waking Thread A. This improves CPU utilization but can fail if the notification is sent before Thread A begins waiting.
④ Piped Communication
This method uses java.io.PipedInputStream and java.io.PipedOutputStream to transfer data between threads, resembling a message‑passing mechanism rather than shared memory.
In distributed systems, the two main communication mechanisms are shared‑memory and message‑based communication. The synchronized keyword and while‑polling belong to shared‑memory communication, while piped streams belong to message‑based communication.
Source: cnblogs.com/hapjin/p/5492619.html
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.