Skip to content

Instantly share code, notes, and snippets.

@paul-bjorkstrand
Last active November 28, 2021 00:17
Show Gist options
  • Save paul-bjorkstrand/8854b1346df290a60ba767977aeaf6e2 to your computer and use it in GitHub Desktop.
Save paul-bjorkstrand/8854b1346df290a60ba767977aeaf6e2 to your computer and use it in GitHub Desktop.
Loom simple actor pattern
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
public class Main {
public static void main(String[] args) {
var a1 = proxy(new TestActor("ta1"), Actor.class);
var a2 = proxy(new TestActor("ta2"), Actor.class);
doStuffWith(a1);
doStuffWith(a2);
a1.performActionOnOtherActor(a2);
a2.performActionOnOtherActor(a1);
a1.performActionOnOtherActor(a1);
}
static <T, U> U proxy(T proxied, Class<U> clazz) {
return (U)
Proxy.newProxyInstance(
proxied.getClass().getClassLoader(),
new Class[] {clazz},
new VirtualThreadInvocationHandler(proxied));
}
static void doStuffWith(Actor proxy) {
proxy.performAction();
System.out.println("finished performAction");
var value = proxy.getValue();
System.out.printf("returned from getValue: %s%n", value);
var calculatedValue = proxy.calculateValue(2);
System.out.printf("returned from calculateValue: %d%n", calculatedValue);
}
}
interface Actor {
void performAction();
String getValue();
int calculateValue(int param);
void performActionOnOtherActor(Actor other);
}
class VirtualThreadInvocationHandler implements InvocationHandler {
final Thread runner;
final BlockingQueue<FutureTask<Object>> queue;
final Object proxied;
volatile boolean stopped;
public VirtualThreadInvocationHandler(Object proxied) {
String name = getClass().getName() + "-" + proxied;
this.runner =
Thread.ofVirtual() //
.name(name)
.unstarted(this::run);
this.queue = new LinkedBlockingQueue<>();
this.proxied = proxied;
runner.start();
}
void run() {
while (!stopped) {
try {
System.out.printf("[%s] run%n", Thread.currentThread().getName());
var task = queue.take();
task.run();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (runner != Thread.currentThread()) {
System.out.printf("[%s] invoke %s%n", Thread.currentThread().getName(), method.getName());
var task = new FutureTask<Object>(() -> method.invoke(proxied, args));
while (!queue.offer(task)) {
// wait until offer returns true
}
return task.get();
} else {
return method.invoke(proxied, args);
}
}
}
class TestActor implements Actor {
final String name;
public TestActor(String name) {
this.name = name;
}
@Override
public void performAction() {
System.out.printf("[%s] performAction%n", Thread.currentThread().getName());
}
@Override
public String getValue() {
System.out.printf("[%s] getValue%n", Thread.currentThread().getName());
return "Test String";
}
@Override
public int calculateValue(int param) {
System.out.printf("[%s] calculateValue: %d%n", Thread.currentThread().getName(), param);
return param * 2;
}
@Override
public void performActionOnOtherActor(Actor other) {
System.out.printf("[%s] performActionOnOtherActor%n", Thread.currentThread().getName());
other.performAction();
}
@Override
public String toString() {
return "TestActor{" + "name='" + name + '\'' + '}';
}
}
[VirtualThreadInvocationHandler-TestActor{name='ta1'}] run
[main] invoke performAction
[VirtualThreadInvocationHandler-TestActor{name='ta2'}] run
[VirtualThreadInvocationHandler-TestActor{name='ta1'}] performAction
[VirtualThreadInvocationHandler-TestActor{name='ta1'}] run
finished performAction
[main] invoke getValue
[VirtualThreadInvocationHandler-TestActor{name='ta1'}] getValue
[VirtualThreadInvocationHandler-TestActor{name='ta1'}] run
returned from getValue: Test String
[main] invoke calculateValue
[VirtualThreadInvocationHandler-TestActor{name='ta1'}] calculateValue: 2
[VirtualThreadInvocationHandler-TestActor{name='ta1'}] run
returned from calculateValue: 4
[main] invoke performAction
[VirtualThreadInvocationHandler-TestActor{name='ta2'}] performAction
[VirtualThreadInvocationHandler-TestActor{name='ta2'}] run
finished performAction
[main] invoke getValue
[VirtualThreadInvocationHandler-TestActor{name='ta2'}] getValue
[VirtualThreadInvocationHandler-TestActor{name='ta2'}] run
returned from getValue: Test String
[main] invoke calculateValue
[VirtualThreadInvocationHandler-TestActor{name='ta2'}] calculateValue: 2
[VirtualThreadInvocationHandler-TestActor{name='ta2'}] run
returned from calculateValue: 4
[main] invoke performActionOnOtherActor
[VirtualThreadInvocationHandler-TestActor{name='ta1'}] performActionOnOtherActor
[VirtualThreadInvocationHandler-TestActor{name='ta1'}] invoke performAction
[VirtualThreadInvocationHandler-TestActor{name='ta2'}] performAction
[VirtualThreadInvocationHandler-TestActor{name='ta2'}] run
[VirtualThreadInvocationHandler-TestActor{name='ta1'}] run
[main] invoke performActionOnOtherActor
[VirtualThreadInvocationHandler-TestActor{name='ta2'}] performActionOnOtherActor
[VirtualThreadInvocationHandler-TestActor{name='ta2'}] invoke performAction
[VirtualThreadInvocationHandler-TestActor{name='ta1'}] performAction
[VirtualThreadInvocationHandler-TestActor{name='ta1'}] run
[VirtualThreadInvocationHandler-TestActor{name='ta2'}] run
[main] invoke performActionOnOtherActor
[VirtualThreadInvocationHandler-TestActor{name='ta1'}] performActionOnOtherActor
[VirtualThreadInvocationHandler-TestActor{name='ta1'}] performAction
[VirtualThreadInvocationHandler-TestActor{name='ta1'}] run
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment