2

1): Will a single threaded application only ever use 1 thread on the user's CPU? Will giving more threads use multiple CPU cores? What happens if you declare more threads than the user's CPU has?

2): As long as there aren't synchronization issues or etc for your methods, is there any performance detriment to creating new threads for each of your methods?

3): I'm making a game, and occasionally there is a noticeable stutter when the garbage is collected. Would putting the garbage collector in its own thread fix this issue?

  • The garbage collector does run in its own thread (or threads). And though all GC implementations have a "stop-the-world" moment, tuning GC can have a massive effect. (Creating less garbage in the first place also helps. :)) This answers the first question too: no, even a "single threaded" Java application will have multiple threads running. – biziclop Mar 08 '16 at 21:24
  • But the main problem I see is that you think using multiple threads is a solution to a performance problem. In fact it very rarely is, and throwing more and more threads at a problem can very quickly start to **reduce** your performance. – biziclop Mar 08 '16 at 21:27
  • Don't call `System.gc();` a bunch, that will cause performance problems, let java do it on its own. – BitNinja Mar 08 '16 at 21:28
  • I know not to call it often, but I was noticing the stutter so to test it I binded System.gc(); to a key, and sure enough when I pressed it there was the stutter. – 2ARSJcdocuyVu7LfjUnB Mar 08 '16 at 21:47

2 Answers2

3

The questions you DID ask:

  1. Yes, one thread means one CPU thread. Additional threads can use additional CPUs, as long as they have work to do. If you create more threads than CPUs, they will fight for CPU time and the result is often slower. Some CPUs use hyperthreading and expose more threads than actual cores (sometimes this gives a performance benefit). Use Runtime.getRuntime.availableProcessors() to show the suggested thread count.
  2. Yes, threads are not cheap to create and destroy, and there is a large overhead for each thread. Pooling and reusing threads is an extremely common pattern for this reason. Don't make a thread for every method call.
  3. The garbage collector already does its work in separate threads, but it will have to cause a "stop the world" pause for at least part of the GC in order to mark dead objects for collection.

The question and answers you need, to the question you didn't ask:

Q: How do I make this go faster?

A: Several ways:

  • Profile: find where the bottleneck is in your program and focus on that. 10% of your code usually consumes 90% of run time.
  • Focus on algorithms before you try to optimize implementations. The only thing better than doing an expensive operation 2x as fast is never having to do it. Lazy loading and caching results can be huge time-savers.
  • Use one main thread for rendering and user interaction, and do as little real work as possible here. This makes the system appear responsive to users. Rendering should only create new objects when something changes.
  • Use a work queue and pool of worker threads for long running tasks especially network requests or disk I/O that would block the rendering and UI thread. All your real work should be asynchronous.
  • Generate as little garbage as possible - don't make and destroy objects when you don't need to. Garbage collection should rarely be a significant bottleneck for a Java program.
  • Use a single thread for disk I/O, multiple threads in a pool for network I/O, and one worker thread per CPU.

Edit: Before someone complains, async I/O or nonblocking I/O perform better than thread pools for network requests, but they're also more complex for a new developer to use correctly. As long as you're not writing something heavy on networking, a modestly-sized thread pool is enough to saturate a network connection despite latency.

If you're getting noticeable garbage collection pauses, then you're probably doing something wrong. Garbage collection is optimized to get the most results for the least time. Most of the time, GC pauses will be a few milliseconds to do a minor GC (unless we're talking about 4+ GB of heap). Do not explicitly call System.gc() or you'll force unneeded work and a big halt-the-world pause.

Community
  • 1
  • 1
BobMcGee
  • 19,824
  • 10
  • 45
  • 57
2

1) Your application may use only 1 thread, in addition to that the JVM may create its own threads (for instance, garbage collection is spun off in its own thread (or more)). More threads use more CPUs, if you have more threads than CPU the scheduler makes the threads take turns (context switching).

2) Excessive context switches (from the scheduler having to go back and forth between all the threads you created and give them a chance to run) and running out of OS threads (either creating too many of them, or losing them due to their never finishing a task) will cause performance problems.

3) The garbage collector is already in its own thread (or multiple threads). Tuning GC and avoiding creating excessive garbage are both good tips.

See Amdahl's Law for limits to how much threads can speed up a task. You excluded locking from the question but excessive locking is a major impediment to performance.

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276