concurrent
Exchanger example passing logs to a background logger
With this example we are going to demonstrate how to create an Exchanger passing logs to a background logger. We have implemented a class, BackgroundLogger
that implements the Runnable interface. In short the class is described below:
- It consists of a static class
LogEntry
, that has three fields, along
time, anint
value and a Stringbuilder, and a static classLogEntries
, that has two fields, an array of specifiedLongEntry
objects, and anint
used field. - The
BackgroundLogger
class creates a final ExecutorService that uses a single worker thread, usingnewSingleThreadExecutor()
API method of Executors. It also creates a final Exchanger ofLogEntries
, that is a synchronization point at which threads can pair and swap elements within pairs. - The classe’s constructor uses
submit(Runnable task)
method of the classse’s ExecutorService to submit this Runnable task for execution and return a Future representing the task. - The classe’s
run()
method, inheritted from the Runnable, is overriden, using the@Override
annotation. In this method theLogEntries
of this task are exchanged, using theexchange(LogEntries x)
API method of the task’s Exchanger. For each one of the logEntry objects used thedelete(int start, int end)
API method of the StringBuilder is used, in order to remove the used entries. - In order that the task stops it first uses its
flush()
method, where the Exchanger is used to exhange any used entries and then theshutdownNow()
API method of ExecutorService is called, so that the task’s excecutor attempts to stop the task.
Let’s take a look at the code snippet that follows:
import java.util.concurrent.Exchanger; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class BackgroundLogger implements Runnable { static final int ENTRIES = 64; static class LogEntry { long time; int level; final StringBuilder text = new StringBuilder(); } static class LogEntries { final LogEntry[] lines = new LogEntry[ENTRIES]; int used = 0; } private final ExecutorService executor = Executors.newSingleThreadExecutor(); final Exchanger<LogEntries> logEntriesExchanger = new Exchanger<LogEntries>(); LogEntries entries = new LogEntries(); BackgroundLogger() { executor.submit(this); } public StringBuilder log(int level) { try { if (entries.used == ENTRIES) entries = logEntriesExchanger.exchange(entries); LogEntry le = entries.lines[entries.used++]; le.time = System.currentTimeMillis(); le.level = level; return le.text; } catch (InterruptedException e) { throw new RuntimeException(e); } } public void flush() throws InterruptedException { if(entries.used > 0) entries = logEntriesExchanger.exchange(entries); } public void stop() { try { flush(); } catch (InterruptedException e) { e.printStackTrace(); // use standard logging. } executor.shutdownNow(); } @Override public void run() { LogEntries entries = new LogEntries(); try { while (!Thread.interrupted()) { entries = logEntriesExchanger.exchange(entries); for (int i = 0; i < entries.used; i++) { bgLog(entries.lines[i]); entries.lines[i].text.delete(0, entries.lines[i].text.length()); } entries.used = 0; } } catch (InterruptedException ignored) { } finally { System.out.println("Warn: logger stopping."); // use standard logging. } } private void bgLog(LogEntry line) { // log the entry to a file. } }
This was an example of how to create an Exchanger passing logs to a background logger in Java.
Related Article:
Reference: The Exchanger and GC-less Java from our JCG partner Peter Lawrey at the Vanilla Java