If I understand your problem, one way would be to adaptively sleep the threads, similarly as video playback is done in Java. If you know you want 50% core utilization, the your algorithm should sleep approximately 0.5 seconds - potentially distributed within a second (e.g. 0.25 sec computation, 0.25 sec sleep, e.t.c.). Here is an example from my video player.
long starttime = 0; // variable declared
//...
// for the first time, remember the timestamp
if (frameCount == 0) {
starttime = System.currentTimeMillis();
}
// the next timestamp we want to wake up
starttime += (1000.0 / fps);
// Wait until the desired next time arrives using nanosecond
// accuracy timer (wait(time) isn't accurate enough on most platforms)
LockSupport.parkNanos((long)(Math.max(0,
starttime - System.currentTimeMillis()) * 1000000));
This code will sleep based on the frames/second value.
To throttle the memory usage, you could wrap your object creation into a factory method, and use some kind of semaphore with a limited permits as bytes to limit the total estimated object size (you need to estimate the size of various objects to ration the semaphore).
package concur;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class MemoryLimited {
private static Semaphore semaphore = new Semaphore(1024 * 1024, true);
// acquire method to get a size length array
public static byte[] createArray(int size) throws InterruptedException {
// ask the semaphore for the amount of memory
semaphore.acquire(size);
// if we get here we got the requested memory reserved
return new byte[size];
}
public static void releaseArray(byte[] array) {
// we don't need the memory of array, release
semaphore.release(array.length);
}
// allocation size, if N > 1M then there will be mutual exclusion
static final int N = 600000;
// the test program
public static void main(String[] args) {
// create 2 threaded executor for the demonstration
ExecutorService exec = Executors.newFixedThreadPool(2);
// what we want to run for allocation testion
Runnable run = new Runnable() {
@Override
public void run() {
Random rnd = new Random();
// do it 10 times to be sure we get the desired effect
for (int i = 0; i < 10; i++) {
try {
// sleep randomly to achieve thread interleaving
TimeUnit.MILLISECONDS.sleep(rnd.nextInt(100) * 10);
// ask for N bytes of memory
byte[] array = createArray(N);
// print current memory occupation log
System.out.printf("%s %d: %s (%d)%n",
Thread.currentThread().getName(),
System.currentTimeMillis(), array,
semaphore.availablePermits());
// wait some more for the next thread interleaving
TimeUnit.MILLISECONDS.sleep(rnd.nextInt(100) * 10);
// release memory, no longer needed
releaseArray(array);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
// run first task
exec.submit(run);
// run second task
exec.submit(run);
// let the executor exit when it has finished processing the runnables
exec.shutdown();
}
}