Tuesday, May 5, 2020

How to use Exchanger in Java with Example

Oracle Java Study Material, Oracle Java Tutorial and Material, Oracle Java Learning

If you are working in a concurrent Java application then you might have heard about the Exchanger class of java.util.concurrent package. The Exchanger in Java is another concurrency or synchronization utility introduced in Java 1.5 along with CountDownLatch, CyclicBarrier, and Semaphores. As the name suggests, the Exchanger allows two Threads to meet and exchange data at the rendezvous or meeting point. The java.util.Exchanger is a parametric class, which defines and holds the type of object to be exchanged. It has an overloaded method called the exchange(), which is used to exchange objects between threads.

This is a blocking method, which means the Thread, which calls the exchange() method wait at exchange point until another Thread arrives. Once another thread arrives, both exchange objects and return from this method. An overloaded version of the exchange method accepts additional TimeUnit object and wait until time out.

By the way, you can also interrupt a Thread waiting at the exchange point for other participants. Unlike CountDownLatch, CyclicBarrier, or Semaphore, the  Exchanger utility can only synchronize two threads, which makes it ideal for solving the classical producer-consumer problem.

In this Java Concurrency tutorial, you will learn how to use Exchanger in Java by implementing a producer-consumer design pattern using Exchanger. Btw, I am assuming that you are familiar with Java programming syntax and semantics, if you are a complete beginner to Java then you may find it difficult to understand this example.

Java Exchanger Example


The Exchanger class is rather a simple synchronization utility to understand and use. In the last couple of concurrency tutorials, we have solved producer consumers using wait and notify and also implemented producer-consumer using BlockingQueue, now it’s time to use Exchanger to implement the same.

In this Java concurrency tutorial, we will be creating one producer and one consumer thread, which will exchange the buffer using the Exchanger utility class.

In general, here is how Exchanger works :

1. You first create an Exchange object like Exchanger<Deque<Long>> stringExchanger = new Exchanger<>(); this defines what type of object will be exchanged between threads. In this case, two threads will exchange the Deque object, containing long values.

2. When Thread A is ready to exchange its buffer or object, it calls the
Exchanger.exchange() method. This is a blocking method, and Thread A will be blocked until Thread B come and transfer its objects to Thread A or Thread A is interrupted or timeout.

3. When Thread B is ready, it also calls the exchange() method. Now both Thread A and B exchange each other’s object and return from the exchange method.

4. Once the exchange completes, Thread A has Thread B’s object and vice-versa.

On the same note, I would like to emphasize the importance of Java concurrency skills and urge every Java developer to spend some time mastering Java concurrent classes.

Oracle Java Study Material, Oracle Java Tutorial and Material, Oracle Java Learning

Java Program with Exchanger in Concurrency


import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.Exchanger;
  
/**
   * Exchanger Example in Java. Exchanger allows two Threads to meet at exchange
   * point and exchange data structure or objects. In this Java program, exchanger
   * is used to exchange buffer between producer and consumer.
  
  */
  
public class JavaExchangerTutorail {
   
    public static void main(String args[]) throws InterruptedException {
  
        //Creating Exchanger to exchange String object with other thread
         final Exchanger> exchanger = new Exchanger>();
   
        Thread producer = new Thread("Producer : "){
  
            @Override
             public void run(){
  
                Deque stack = new ArrayDeque();
  
                //producer thread insert elments into stack 
                while (stack.isEmpty()) {

                    stack.add(System.nanoTime()%1000);  
                    //if stack is not empty then exchange it to consumer thread
  
                    try {
  
                        System.out.println(Thread.currentThread().getName() 
                                 + " ready to exchange : " + stack);

<br>

// Exchanger return other Thread's object
                        stack = exchanger.exchange(stack); 
                        System.out.println(Thread.currentThread().getName() 
                                 + " got : " + stack);
  
                    } catch (InterruptedException ie) { ie.printStackTrace(); }
  
                }
  
            }
  
        };
  
        Thread consumer = new Thread("Consumer : "){
  
            @Override
  
            public void run(){
  
                Deque stack = new ArrayDeque();
   
                //consumer thread takes object from stack and prints
  
                do{
                     //if stack is empty then exchange it to producer for refill
                     try {
  
                        System.out.println(Thread.currentThread().getName() 
                                 + " ready to exchange : " + stack); 
                        stack = exchanger.exchange(stack); 
                        System.out.println(Thread.currentThread().getName() 
                                 + " got : " + stack); 
                        stack.remove();
  
                    } catch (InterruptedException ie) { ie.printStackTrace(); }
  
                }while(stack.isEmpty()) ;       
    
            }
  
        };
    
        producer.start(); 

<br>

//sleeping before starting consumer to give producer time to produce
        Thread.sleep(1000);

consumer.start();
  
    }
  
}
  
Output:
  
Producer :  ready to exchange : [247]
  
Consumer :  ready to exchange : []
  
Producer :  got : []
  
Consumer :  got : [247]
  
Producer :  ready to exchange : [692]
  
Consumer :  ready to exchange : []
  
Consumer :  got : [692]
  
Consumer :  ready to exchange : []
  
Producer :  got : []

<br>

Explanation of Code and Output


If you look at the above example, all code is inside the main method. We have made the Exchanger instance final because we are accessing them from Anonymous inner class, and only final local variables are accessible from the anonymous inner class.

Later, we created two threads, Producer and Consumer. The producer checks the queue and if it’s empty, it adds the last three digits of current nano time and calls the exchange() method.

Now, until the Consumer thread arrives at the exchange point, I mean until it calls the exchange() method, the Producer thread will be blocked.

Once a consumer arrives, both exchange each other’s stack and return from the exchange() method. At this time, the Producer has an empty stack of consumers and the consumer has a non-empty stack of Producer, I mean, they have each other’s object.

For understanding, which thread is exchanging which stack, we print the content of stack before and after an exchange on each thread. If you look at the output, it’s self-explanatory.

By the way, as with threads, you are not guaranteed to get the output in the same order. In the third iteration, you can see the consumer has an emptied stack and ready to exchange empty stack even before producer thread gets scheduled and return from the exchange method.

Source: javacodegeeks.com

Related Posts

0 comments:

Post a Comment