Now we will look at how a ThimbleExecutor handles coalescing tasks. Coalescing tasks are tasks where a later task can overwrite a previous submitted task that has not executed yet.
The code below executes 100 coalescing tasks in a 4-thread ThimbleExecutor.
:::java
public class TestCoalescing
{
public static void main(String[] args) throws Exception
{
Executor executor = new ThimbleExecutor(4);
final int count = 100;
final CountDownLatch latch = new CountDownLatch(count * 2);
for (int i = 0; i < count; i++)
{
final int myCount = i;
executor.execute(new ICoalescingRunnable()
{
@Override
public void run()
{
System.out.println("HelloWorld (context 1) " + myCount);
latch.countDown();
}
@Override
public Object context()
{
return "context1";
}
});
executor.execute(new ICoalescingRunnable()
{
@Override
public void run()
{
System.out.println("HelloWorld (context 2) " + myCount);
latch.countDown();
}
@Override
public Object context()
{
return "context2";
}
});
}
latch.await();
}
}
The output shows that not every submitted task is run but it is always a later task that is run (never a former one). Indeed only about 1/10th of the tasks per context are executed. Coalescing tasks are useful in situations where a high-frequency producer is scheduling tasks where logically only the latest task at any point in time needs to be executed. This can drastically improve throughput performance.
HelloWorld (context 1) 0
HelloWorld (context 2) 4
HelloWorld (context 1) 28
HelloWorld (context 1) 32
HelloWorld (context 2) 35
HelloWorld (context 2) 42
HelloWorld (context 1) 38
HelloWorld (context 2) 50
HelloWorld (context 1) 56
HelloWorld (context 1) 59
HelloWorld (context 1) 61
HelloWorld (context 1) 67
HelloWorld (context 2) 67
HelloWorld (context 1) 72
HelloWorld (context 2) 82
HelloWorld (context 2) 89
HelloWorld (context 2) 95
HelloWorld (context 1) 97
HelloWorld (context 2) 99
HelloWorld (context 1) 99
Compare with using a standard 4-thread executor.
:::java
public class TestCoalescingWithStandardExecutor
{
public static void main(String[] args) throws Exception
{
Executor executor = Executors.newFixedThreadPool(4);
final int count = 100;
final CountDownLatch latch = new CountDownLatch(count * 2);
for (int i = 0; i < count; i++)
{
final int myCount = i;
executor.execute(new ICoalescingRunnable()
{
@Override
public void run()
{
System.out.println("HelloWorld (context 1) " + myCount);
latch.countDown();
}
@Override
public Object context()
{
return "context1";
}
});
executor.execute(new ICoalescingRunnable()
{
@Override
public void run()
{
System.out.println("HelloWorld (context 2) " + myCount);
latch.countDown();
}
@Override
public Object context()
{
return "context2";
}
});
}
latch.await();
}
}
The output shows that every task is run and also not in order.
HelloWorld (context 1) 0
HelloWorld (context 1) 2
HelloWorld (context 2) 2
HelloWorld (context 1) 3
HelloWorld (context 2) 3
HelloWorld (context 1) 4
HelloWorld (context 2) 4
HelloWorld (context 1) 5
HelloWorld (context 2) 5
HelloWorld (context 1) 6
HelloWorld (context 2) 6
...
HelloWorld (context 2) 96
HelloWorld (context 1) 97
HelloWorld (context 2) 97
HelloWorld (context 1) 98
HelloWorld (context 2) 98
HelloWorld (context 1) 99
HelloWorld (context 2) 99
HelloWorld (context 1) 69
HelloWorld (context 1) 75
HelloWorld (context 2) 74
Executing coalescing tasks using a ThimbleExecutor provides an efficient way of handling fast-producer situations where the data being produced can be coalesced.