code: public static void init(
  		ThreadFactory threadFactory,
  		Class<? extends TaskPriorityManager> taskPriorityManager,
  		int nbCPUThreads,
  		DrivesProvider drivesProvider,
  		int nbUnmanagedThreads
  	) throws IllegalStateException {
  		if (isInitialized()) throw new IllegalStateException("Threading has been already initialized.");
  		logger = LCCore.get().getThreadingLogger();
  		TaskScheduler.init();
  		cpu = new CPUTaskManager(threadFactory, taskPriorityManager, nbCPUThreads);
  		cpu.start();
  		resources.put(CPU, cpu);
  		drives = new DrivesTaskManager(threadFactory, taskPriorityManager, drivesProvider);
  		unmanaged = new ThreadPoolTaskManager("Unmanaged tasks manager", UNMANAGED, nbUnmanagedThreads, threadFactory, taskPriorityManager);
  		resources.put(UNMANAGED, unmanaged);
  		LCCore.get().toClose(new StopMultiThreading());
  		if (traceTasksNotDone)
  			ThreadingDebugHelper.traceTasksNotDone();
  		TaskMonitoring.start(threadFactory);
  		synchronized (resources) {
  			for (TaskManager tm : resources.values())
  				tm.started();
  		}
  	}
docs: Initialize multi-threading.
  This method is called by the {@link net.lecousin.framework.application.LCCore.Environment} instance on initialization.
  @param threadFactory factory to use when creating threads
  @param taskPriorityManager the class to use to manage priority of tasks
  @param nbCPUThreads number of threads to use for CPU tasks,
  0 or negative value means the number of available processors returned by {@link Runtime#availableProcessors()}
  @param drivesProvider provides physical drives and associated mount points.
  If null, {@link File#listRoots()} is used and each returned root is considered as a physical drive.
  This may be changed later on by calling {@link DrivesTaskManager#setDrivesProvider(DrivesProvider)}.
  @param nbUnmanagedThreads number of threads to use for unmanaged tasks.
  If 0 or negative, maximum 100 threads will be used.