Skip to content

Commit dc4904c

Browse files
committed
Changed the implementation for better understanding
1 parent 4c22055 commit dc4904c

File tree

5 files changed

+117
-55
lines changed

5 files changed

+117
-55
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.iluwatar.halfsynchalfasync;
2+
3+
import java.util.concurrent.Callable;
4+
5+
/**
6+
* Represents some computation that is performed asynchronously. The computation is typically
7+
* done is background threads and the result is posted back in form of callback.
8+
*
9+
* @param <O> type of result
10+
*/
11+
public interface AsyncTask<O> extends Callable<O> {
12+
/**
13+
* Is called in context of caller thread before call to {@link #call()}.
14+
* Validations can be performed here so that the performance penalty of context
15+
* switching is not incurred.
16+
*/
17+
void preExecute();
18+
19+
/**
20+
* Is a callback which is called after the result is successfully computed by
21+
* {@link #call()}.
22+
*/
23+
void onResult(O result);
24+
25+
void onError(Throwable throwable);
26+
27+
@Override
28+
O call() throws Exception;
29+
}
Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.iluwatar.halfsynchalfasync;
22

33
import java.util.concurrent.BlockingQueue;
4-
import java.util.concurrent.Callable;
54
import java.util.concurrent.Future;
65
import java.util.concurrent.ThreadPoolExecutor;
76

@@ -12,40 +11,27 @@
1211
* picks up the task and executes it in background and the result is posted back to the caller via
1312
* {@link Future}.
1413
*/
15-
public abstract class AsynchronousService<I, O> {
14+
public class AsynchronousService {
1615

1716
/*
18-
* This is the synchronous layer to which request to do work is submitted.
17+
* This is the synchronous layer to which request to do work is delegated.
1918
*/
20-
private SynchronousLayer syncLayer = new SynchronousLayer();
19+
private SynchronousLayer syncLayer;
20+
21+
public AsynchronousService(QueuingLayer queuingLayer) {
22+
this.syncLayer = new SynchronousLayer(queuingLayer);
23+
}
2124

2225
/**
23-
* Computes arithmetic sum for n
2426
*
25-
* @return future representing arithmetic sum of n
2627
*/
27-
public Future<O> execute(final I input) {
28+
public void execute(final AsyncTask<?> task) {
2829
/*
2930
* This is the key part of this pattern where the caller thread does not block until
3031
* the result of work is computed but is delegated to the synchronous layer which
3132
* computes the task in background. This is useful if caller thread is an UI thread,
3233
* which MUST remain responsive to user inputs.
3334
*/
34-
return syncLayer.submit(new Callable<O>() {
35-
36-
@Override
37-
public O call() throws Exception {
38-
return doInBackground(input);
39-
}
40-
41-
});
35+
syncLayer.execute(task);
4236
}
43-
44-
/**
45-
* This method is called in context of background thread where the implementation should compute
46-
* and return the result for input.
47-
*
48-
* @return computed result
49-
*/
50-
protected abstract O doInBackground(I input);
5137
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.iluwatar.halfsynchalfasync;
2+
3+
import java.util.concurrent.BlockingQueue;
4+
import java.util.concurrent.LinkedBlockingQueue;
5+
6+
public class QueuingLayer {
7+
BlockingQueue<Runnable> incomingQueue = new LinkedBlockingQueue<>();
8+
}
Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,54 @@
11
package com.iluwatar.halfsynchalfasync;
22

3-
import java.util.concurrent.Callable;
3+
import java.util.concurrent.ExecutionException;
44
import java.util.concurrent.ExecutorService;
5-
import java.util.concurrent.Future;
6-
import java.util.concurrent.LinkedBlockingQueue;
5+
import java.util.concurrent.FutureTask;
76
import java.util.concurrent.ThreadPoolExecutor;
87
import java.util.concurrent.TimeUnit;
98

109
/**
1110
* This represents the Queuing and Synchronous layer of Half-Sync/Half-Async pattern.
12-
* The incoming requests are queued and then picked up by the background threads for execution.
11+
* The {@link ThreadPoolExecutor} plays role of both Queuing layer as well as Synchronous layer
12+
* of the pattern, where incoming tasks are queued if no worker is available.
1313
*/
1414
public class SynchronousLayer {
1515

1616
/*
17-
* This is the queuing layer where incoming work is queued
17+
* This is the synchronous layer where background threads execute the work.
1818
*/
19-
private LinkedBlockingQueue<Runnable> tasks = new LinkedBlockingQueue<Runnable>();
20-
/*
21-
* This is the synchronous layer where background threads execute the work
22-
*/
23-
private ExecutorService service = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, tasks);
19+
private ExecutorService service;
2420

21+
/**
22+
* Creates synchronous layer which uses queuing layer to wait for incoming tasks to execute.
23+
*/
24+
public SynchronousLayer(QueuingLayer queuingLayer) {
25+
service = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, queuingLayer.incomingQueue);
26+
}
2527
/**
2628
* Submit new work for backgrounds threads to compute
2729
* @return the result after executing the work
2830
*/
29-
public <T> Future<T> submit(Callable<T> work) {
30-
return service.submit(work);
31+
public <T> void execute(final AsyncTask<T> work) {
32+
work.preExecute();
33+
34+
service.submit(new FutureTask<T>(work) {
35+
@Override
36+
protected void done() {
37+
super.done();
38+
try {
39+
/* called in context of background thread. There is other variant possible
40+
* where result is posted back and sits in the queue of caller thread which
41+
* then picks it up for processing. An example of such a system is Android OS,
42+
* where the UI elements can only be updated using UI thread. So result must be
43+
* posted back in UI thread.
44+
*/
45+
work.onResult(get());
46+
} catch (InterruptedException e) {
47+
// should not occur
48+
} catch (ExecutionException e) {
49+
work.onError(e.getCause());
50+
}
51+
}
52+
});
3153
}
3254
}
Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package com.iluwatar.halfsynchalfasync;
22

3+
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.fail;
5+
36
import java.util.concurrent.ExecutionException;
4-
import java.util.concurrent.Future;
57

68
import org.junit.Test;
7-
import static org.junit.Assert.*;
89

910
public class AsynchronousServiceTest {
1011

@@ -14,32 +15,48 @@ public void test() throws InterruptedException, ExecutionException {
1415
* Addition service is asynchronous layer which does not block on single request,
1516
* and is always available for listening new requests.
1617
*/
17-
ArithmeticSumService service = new ArithmeticSumService();
18-
Future<Long> output1 = service.execute(100L);
19-
Future<Long> output2 = service.execute(50L);
20-
Future<Long> output3 = service.execute(200L);
21-
Future<Long> output4 = service.execute(5L);
18+
QueuingLayer queuingLayer = new QueuingLayer();
19+
new SynchronousLayer(queuingLayer);
20+
AsynchronousService service = new AsynchronousService(queuingLayer);
2221

23-
assertEquals(ap(100), output1.get().longValue());
24-
assertEquals(ap(50), output2.get().longValue());
25-
assertEquals(ap(200), output3.get().longValue());
26-
assertEquals(ap(5), output4.get().longValue());
22+
service.execute(new ArithmeticSumTask(100));
23+
service.execute(new ArithmeticSumTask(50));
24+
service.execute(new ArithmeticSumTask(200));
25+
service.execute(new ArithmeticSumTask(5));
2726
}
2827

29-
/*
30-
* This is an asynchronous service which computes arithmetic sum
31-
*/
32-
class ArithmeticSumService extends AsynchronousService<Long, Long> {
28+
class ArithmeticSumTask implements AsyncTask<Long> {
29+
private long n;
3330

31+
public ArithmeticSumTask(long n) {
32+
this.n = n;
33+
}
34+
3435
@Override
35-
protected Long doInBackground(Long n) {
36-
return (n) * (n + 1) / 2;
36+
public Long call() throws Exception {
37+
return ap(n);
38+
}
39+
40+
@Override
41+
public void preExecute() {
42+
if (n < 0) {
43+
throw new IllegalArgumentException("n is less than 0");
44+
}
3745
}
38-
}
3946

40-
private long ap(int i) {
47+
@Override
48+
public void onResult(Long result) {
49+
assertEquals(ap(n), result.longValue());
50+
}
51+
52+
@Override
53+
public void onError(Throwable throwable) {
54+
fail("Should not occur");
55+
}
56+
}
57+
58+
private long ap(long i) {
4159
long out = (i) * (i + 1) / 2;
42-
System.out.println(out);
4360
return out;
4461
}
4562
}

0 commit comments

Comments
 (0)