public class Test {
public static void main(String[] args) throws InterruptedException {
TestLib lib = Native.loadLibrary("hello", TestLib.class);
for (int i = 0; i < 10; i++) {
lib.registerCallback(new TestCallback());
}
new Thread(() -> {
while (true) {
try {
System.out.println("-------------------" + TestCallback.i.getAndSet(0) + " === " );
lib.printInfo();
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
for (int i = 1; i <= 32; i++) {
new Thread(() -> lib.start()).start();
}
}
public static class TestCallback implements Callback {
static AtomicLong i = new AtomicLong(0);
public void callback(ArrayStruct hdrs) {
i.addAndGet(hdrs.size);
}
}
public static class ArrayStruct extends Structure {
public int size;
public TestStruct.ByReference list;
public ArrayStruct() {
}
public ArrayStruct(Pointer peer) {
super(peer);
read();
}
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("size", "list");
}
}
public static class TestStruct extends Structure {
public String strField;
public int intField;
public String getStrField() {
return strField;
}
public void setStrField(String strField) {
this.strField = strField;
}
public TestStruct() {
}
public TestStruct(Pointer peer) {
super(peer);
read();
}
public int getIntField() {
return intField;
}
public void setIntField(int intField) {
this.intField = intField;
}
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("strField", "intField");
}
public static class ByReference extends TestStruct implements Structure.ByReference {}
}
}
lib.start()
)package ex.ample;
import java.util.ArrayList;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
public class Main {
public static void main(String[] args) {
//TestLib lib = Native.loadLibrary("hello", TestLib.class);
final TestLib lib = new TestLib();
for (int i = 0; i < 5; i++) {
lib.registerCallback( new TestCallback() );
}
new Thread(() -> {
while (true) {
try {
String msg = "-------------------" + TestCallback.counter.get() + " ===\n";
msg += lib.printInfo();
System.out.println(msg + "\n-RESET COUNTER ----" + TestCallback.counter.getAndSet(0) + "->0 ==" );
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
for (int i = 1; i < 5+1; i++) {
new Thread(() -> lib.start()).start();
}
System.out.println("---\nExiting main()...\n---");
}
interface ICallback {
void callback(int inc);
String printStatistic();
}
public static class TestCallback implements ICallback{
static AtomicLong counter = new AtomicLong(0);
private final AtomicLong totalIncCounter = new AtomicLong(0);
@Override
public void callback(int inc) {
counter.addAndGet(inc);
totalIncCounter.addAndGet(1L);
}
@Override
public String printStatistic() {
return "cb#" + this.hashCode() + " \tcalled " + totalIncCounter.get() + " times,\tcurrently at "+ counter.get();
}
}
public static class TestLib {
private final ArrayList<ICallback> registeredCallbacks = new ArrayList<>();
public void registerCallback(Main.ICallback cb){
registeredCallbacks.add(cb);
}
public String printInfo(){
String res = "";
for(Main.ICallback cb : registeredCallbacks){
res += cb.printStatistic()+"\n";
}
return res;
}
public void start(){
while(true){
for(Main.ICallback cb : registeredCallbacks){
try {
TimeUnit.MILLISECONDS.sleep(ThreadLocalRandom.current().nextInt(300, 700+1) );
int incrementThisTime = ThreadLocalRandom.current().nextInt(1, 5+1);
String msg = "Thread " + Thread.currentThread().getId() + "\nbefore\t" + cb.printStatistic() + "\tincrement " + incrementThisTime;
cb.callback(incrementThisTime);
System.out.println(msg + "\nafter\t" + cb.printStatistic());
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
}
}
}
}
If your callback needs to live beyond the method invocation where it is used, make sure you keep a reference to it or the native code will call back to an empty stub after the callback object is garbage collected