Update: after discussing with the question owner, here is the final proposed solution, with code:
package toys;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class TwoQueues {
//tweak it for your purpose.
private final int CPU_COUNT = 4;
private BlockingQueue<Runnable> lightTaskQueue = new LinkedBlockingDeque<Runnable>();
private ThreadPoolExecutor lightExecutor = new ThreadPoolExecutor(CPU_COUNT, CPU_COUNT, 60L, TimeUnit.SECONDS, lightTaskQueue);
private BlockingQueue<Runnable> heavyTaskQueue = new LinkedBlockingDeque<Runnable>();
private ThreadPoolExecutor heavyExecutor = new ThreadPoolExecutor(1, 1, 60L, TimeUnit.SECONDS, heavyTaskQueue);
public static class SampleLightTask implements Runnable {
@Override
public void run() {
System.out.println("I am " + this + " and running fast!");
}
}
private static AtomicBoolean heavyTaskRunning = new AtomicBoolean();
public static class SampleHeavyTask implements Runnable {
@Override
public void run() {
try {
heavyTaskRunning.set(true);
System.out.println("I am " + this + " and running quite slow!");
final long start = System.currentTimeMillis();
while (true) {
//burn the CPU for ten senconds.
if (System.currentTimeMillis()-start >= 10000L)
break;
}
} finally {
heavyTaskRunning.set(false);;
}
}
}
public void shutDownNow() {
this.lightExecutor.shutdownNow();
this.heavyExecutor.shutdownNow();
}
public void runOrQueueLightTask(SampleLightTask lightOne) {
this.lightExecutor.execute(lightOne);
}
public void runOrQueueHeavyTask(SampleHeavyTask heavyOne) {
if (heavyTaskRunning.get()) {
System.out.println("running, skipped new one: " + heavyOne);
return;
}
this.heavyExecutor.execute(heavyOne);
}
public static void main(String[] args) throws Exception {
TwoQueues q = new TwoQueues();
final long start = System.currentTimeMillis();
//Run the queues for 30 seconds, add CPU-light and CPU-weight tasks
//every second.
while (System.currentTimeMillis()-start<=30*1000L) {
q.runOrQueueHeavyTask(new SampleHeavyTask());
q.runOrQueueLightTask(new SampleLightTask());
Thread.sleep(1000L);
}
q.shutDownNow();
}
}
And the running output:
I am toys.TwoQueues$SampleHeavyTask@6d0cecb2 and running quite slow!
I am toys.TwoQueues$SampleLightTask@6b87d20c and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@2ce07e6b
I am toys.TwoQueues$SampleLightTask@7fa0d111 and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@16fdf48d
I am toys.TwoQueues$SampleLightTask@5fbd7d0e and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@115d533d
I am toys.TwoQueues$SampleLightTask@59c27402 and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@6d4e5d57
I am toys.TwoQueues$SampleLightTask@33d232d1 and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@79ec3264
I am toys.TwoQueues$SampleLightTask@1e081c5 and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@3a67ad79
I am toys.TwoQueues$SampleLightTask@6cae00e3 and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@13bc6ed3
I am toys.TwoQueues$SampleLightTask@380fe8c4 and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@1c7ab89d
I am toys.TwoQueues$SampleLightTask@3cee5a06 and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@44585f2a
I am toys.TwoQueues$SampleLightTask@5cfe174 and running fast!
I am toys.TwoQueues$SampleLightTask@12da89a7 and running fast!
I am toys.TwoQueues$SampleHeavyTask@49833c9c and running quite slow!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@47004b78
I am toys.TwoQueues$SampleLightTask@645ad7b2 and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@8071a97
I am toys.TwoQueues$SampleLightTask@a62b39f and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@55fe910c
I am toys.TwoQueues$SampleLightTask@3be4d6ef and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@2cdb03a1
I am toys.TwoQueues$SampleLightTask@5ecb5608 and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@777d57d6
I am toys.TwoQueues$SampleLightTask@4611dfe3 and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@3f81d405
I am toys.TwoQueues$SampleLightTask@6486b4d5 and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@47ca3f82
I am toys.TwoQueues$SampleLightTask@2f0f94a0 and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@27e6ac83
I am toys.TwoQueues$SampleLightTask@1947e0ec and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@3dffb2eb
I am toys.TwoQueues$SampleLightTask@5e3b8219 and running fast!
I am toys.TwoQueues$SampleLightTask@14da67a4 and running fast!
I am toys.TwoQueues$SampleHeavyTask@eca4aae and running quite slow!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@2eced18
I am toys.TwoQueues$SampleLightTask@10c1c428 and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@213526b0
I am toys.TwoQueues$SampleLightTask@287efdd8 and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@294b84ad
I am toys.TwoQueues$SampleLightTask@1cf38f09 and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@3a33a6b8
I am toys.TwoQueues$SampleLightTask@150697e2 and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@63dd8136
I am toys.TwoQueues$SampleLightTask@634e3372 and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@2313b44d
I am toys.TwoQueues$SampleLightTask@62a23d38 and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@9615a1f
I am toys.TwoQueues$SampleLightTask@5663ae08 and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@2a36bb87
I am toys.TwoQueues$SampleLightTask@6f51b1b7 and running fast!
running, skipped new one: toys.TwoQueues$SampleHeavyTask@5c6a9e79
I am toys.TwoQueues$SampleLightTask@5bca4955 and running fast!
////////////////////////////////// Old Answer ////////////////////////////////////
If I understand your requirement correctly, there are two types of tasks: type A, CPU intensive, executed in a serials manner, and possibly modify some global states that are not thread safe; type B, not CPU intensive, and need to completes them as fast as possible.
Why not using two thread pools and two queues for this? this is a perfect match. For tasks of type A, schedule them in a thread pool with max concurrency set to 1; and for the type B, in another thread pool with max concurrency set to your CPU core/thread numbers or whatever suitable for your need. There is even no need for "check & gracefully fail" here.
I used to write a lot of these primitive, low-level concurrent, threading stuff myself, but the time the thread pool becomes standard library in JDK, I never go back again, both on server side(EE) and client side(Android here). The design is clean, the performance is good, much less code, and of course, much less bugs. Debugging a concurrency related bug is never easy.