I guess your question boils down to measuring the "the exact time that the main thread has been running". In order to get to know this you don't need to measure the blocking time of the garbage collector.
The time a thread has been running corresponds to its CPU time. If you also want to consider the time waiting for I/O, then this approach won't work for you, but then the time the garbage collector pauses your thread should be negligible anyways.
I put together a simple proof of concept for you:
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.util.concurrent.TimeUnit;
public class CpuTime
{
public static void main(String[] args) {
// We obtain the thread mx bean and check if it support CPU time measuring.
final ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean();
if (!threadMxBean.isThreadCpuTimeSupported())
throw new IllegalStateException("CPU time not supported on this platform");
// This is important because the default value is platform-dependent.
threadMxBean.setThreadCpuTimeEnabled(true);
// Now we start the measurement ...
final long threadId = Thread.currentThread().getId();
final long wallTimeStart = System.nanoTime();
final long cpuTimeStart = threadMxBean.getThreadCpuTime(Thread.currentThread().getId());
// ... and do stupid things to give the garbage collector some work.
for (int i = 0; i < 1_000_000_000; i++) {
Long l = new Long(i);
}
// Finally we measure the cpu and wall time.
final long wallTime = System.nanoTime() - wallTimeStart;
final long cpuTime = threadMxBean.getThreadCpuTime(threadId) - cpuTimeStart;
System.out.println("CPU time: " + TimeUnit.NANOSECONDS.toMillis(cpuTime));
System.out.println("Wall time: " + TimeUnit.NANOSECONDS.toMillis(wallTime));
}
}
Please note that this approach needs some tweaking for absolutely precise results. It should however get you started.