Exchanger類在線程之間傳遞工作和回收使用的對象方面非常有效。 AFAIK,它也是最少使用的并發類之一。
但是,如果您不需要GC,則使用ArrayBlockingQueue進行日志記錄會更簡單。
交換器類
Exchanger類對于在兩個線程之間來回傳遞數據很有用。 例如生產者/消費者。 它具有自然回收用于傳遞工作的數據結構的特性,并以有效的方式支持無GC的工作共享。
這是一個將日志傳遞到后臺記錄器的示例。
工作(日志條目)被批處理到LogEntries中,并傳遞給后臺線程,該線程隨后又將其傳遞回線程,以便它可以添加更多工作。 如果后臺線程始終在批處理完成之前完成,則它幾乎是透明的。 批處理大小的增加會減少批處理已滿的頻率,但會增加任何一次等待的未處理條目的數量。 調用flush()可以推出數據。
關鍵行如下,它將當前線程中的批處理與另一個線程中的批處理交換。 生產者在消費者清空時填充批次。
交換發生時通常需要1-4微秒。 在這種情況下,每64行一次。
entries = logEntriesExchanger.exchange(entries);
交換器示例
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();}@Overridepublic 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.}
}
參考:來自Vanilla Java的 JCG合作伙伴 Peter Lawrey 的Exchanger和無GC的 Java 。
- Java中的低GC:使用原語而不是包裝器
- Java Lambda語法替代
- JVM如何處理鎖
- Erlang與Java內存架構
- Java Fork / Join進行并行編程
- Java最佳實踐系列
- 如何在Java中獲得類似于C的性能
翻譯自: https://www.javacodegeeks.com/2011/09/exchanger-and-gc-less-java.html