Forcing multiple threads to use multiple CPUs when they are available

后端 未结 10 976
后悔当初
后悔当初 2020-11-28 19:21

I\'m writing a Java program which uses a lot of CPU because of the nature of what it does. However, lots of it can run in parallel, and I have made my program multi-threade

相关标签:
10条回答
  • 2020-11-28 19:29

    When I run it, it only seems to use one CPU until it needs more then it uses another CPU - is there anything I can do in Java to force different threads to run on different cores/CPUs?

    I interpret this part of your question as meaning that you have already addressed the problem of making your application multi-thread capable. And despite that, it doesn't immediately start using multiple cores.

    The answer to "is there any way to force ..." is (AFAIK) not directly. Your JVM and/or the host OS decide how many 'native' threads to use, and how those threads are mapped to physical processors. You do have some options for tuning. For example, I found this page which talks about how to tune Java threading on Solaris. And this page talks about other things that can slow down a multi-threaded application.

    0 讨论(0)
  • 2020-11-28 19:29

    You should write your program to do its work in the form of a lot of Callable's handed to an ExecutorService and executed with invokeAll(...).

    You can then choose a suitable implementation at runtime from the Executors class. A suggestion would be to call Executors.newFixedThreadPool() with a number roughly corresponding to the number of cpu cores to keep busy.

    0 讨论(0)
  • 2020-11-28 19:35

    I think this issue is related to Java Parallel Proccesing Framework (JPPF). Using this you can run diferent jobs on diferent processors.

    0 讨论(0)
  • 2020-11-28 19:38

    There are two basic ways to multi-thread in Java. Each logical task you create with these methods should run on a fresh core when needed and available.

    Method one: define a Runnable or Thread object (which can take a Runnable in the constructor) and start it running with the Thread.start() method. It will execute on whatever core the OS gives it -- generally the less loaded one.

    Tutorial: Defining and Starting Threads

    Method two: define objects implementing the Runnable (if they don't return values) or Callable (if they do) interface, which contain your processing code. Pass these as tasks to an ExecutorService from the java.util.concurrent package. The java.util.concurrent.Executors class has a bunch of methods to create standard, useful kinds of ExecutorServices. Link to Executors tutorial.

    From personal experience, the Executors fixed & cached thread pools are very good, although you'll want to tweak thread counts. Runtime.getRuntime().availableProcessors() can be used at run-time to count available cores. You'll need to shut down thread pools when your application is done, otherwise the application won't exit because the ThreadPool threads stay running.

    Getting good multicore performance is sometimes tricky, and full of gotchas:

    • Disk I/O slows down a LOT when run in parallel. Only one thread should do disk read/write at a time.
    • Synchronization of objects provides safety to multi-threaded operations, but slows down work.
    • If tasks are too trivial (small work bits, execute fast) the overhead of managing them in an ExecutorService costs more than you gain from multiple cores.
    • Creating new Thread objects is slow. The ExecutorServices will try to re-use existing threads if possible.
    • All sorts of crazy stuff can happen when multiple threads work on something. Keep your system simple and try to make tasks logically distinct and non-interacting.

    One other problem: controlling work is hard! A good practice is to have one manager thread that creates and submits tasks, and then a couple working threads with work queues (using an ExecutorService).

    I'm just touching on key points here -- multithreaded programming is considered one of the hardest programming subjects by many experts. It's non-intuitive, complex, and the abstractions are often weak.


    Edit -- Example using ExecutorService:

    public class TaskThreader {
        class DoStuff implements Callable {
           Object in;
           public Object call(){
             in = doStep1(in);
             in = doStep2(in);
             in = doStep3(in); 
             return in;
           }
           public DoStuff(Object input){
              in = input;
           }
        }
    
        public abstract Object doStep1(Object input);    
        public abstract Object doStep2(Object input);    
        public abstract Object doStep3(Object input);    
    
        public static void main(String[] args) throws Exception {
            ExecutorService exec = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
            ArrayList<Callable> tasks = new ArrayList<Callable>();
            for(Object input : inputs){
               tasks.add(new DoStuff(input));
            }
            List<Future> results = exec.invokeAll(tasks);
            exec.shutdown();
            for(Future f : results) {
               write(f.get());
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-28 19:42

    First, I'd suggest reading "Concurrency in Practice" by Brian Goetz.

    alt text

    This is by far the best book describing concurrent java programming.

    Concurrency is 'easy to learn, difficult to master'. I'd suggest reading plenty about the subject before attempting it. It's very easy to get a multi-threaded program to work correctly 99.9% of the time, and fail 0.1%. However, here are some tips to get you started:

    There are two common ways to make a program use more than one core:

    1. Make the program run using multiple processes. An example is Apache compiled with the Pre-Fork MPM, which assigns requests to child processes. In a multi-process program, memory is not shared by default. However, you can map sections of shared memory across processes. Apache does this with it's 'scoreboard'.
    2. Make the program multi-threaded. In a multi-threaded program, all heap memory is shared by default. Each thread still has it's own stack, but can access any part of the heap. Typically, most Java programs are multi-threaded, and not multi-process.

    At the lowest level, one can create and destroy threads. Java makes it easy to create threads in a portable cross platform manner.

    As it tends to get expensive to create and destroy threads all the time, Java now includes Executors to create re-usable thread pools. Tasks can be assigned to the executors, and the result can be retrieved via a Future object.

    Typically, one has a task which can be divided into smaller tasks, but the end results need to be brought back together. For example, with a merge sort, one can divide the list into smaller and smaller parts, until one has every core doing the sorting. However, as each sublist is sorted, it needs to be merged in order to get the final sorted list. Since this is "divide-and-conquer" issue is fairly common, there is a JSR framework which can handle the underlying distribution and joining. This framework will likely be included in Java 7.

    0 讨论(0)
  • 2020-11-28 19:42

    There is no way to set CPU affinity in Java. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4234402

    If you have to do it, use JNI to create native threads and set their affinity.

    0 讨论(0)
提交回复
热议问题