feat: initial multithreaded downloading
This commit is contained in:
@@ -109,7 +109,7 @@ public final class LauncherHelper {
|
||||
try {
|
||||
checkGameState(profile, setting, version, () -> {
|
||||
Controllers.dialog(launchingStepsPane);
|
||||
Schedulers.newThread().execute(this::launch0);
|
||||
Schedulers.defaultScheduler().execute(this::launch0);
|
||||
});
|
||||
} catch (InterruptedException | RejectedExecutionException ignore) {
|
||||
}
|
||||
@@ -199,7 +199,7 @@ public final class LauncherHelper {
|
||||
Controllers.dialog(i18n("version.launch_script.success", scriptFile.getAbsolutePath()));
|
||||
});
|
||||
}
|
||||
}).thenRunAsync(Schedulers.defaultScheduler(), () -> {
|
||||
}).thenRunAsync(() -> {
|
||||
launchingLatch.await();
|
||||
}).withStage("launch.state.waiting_launching"))
|
||||
.withStagesHint(Lang.immutableListOf(
|
||||
|
||||
@@ -34,6 +34,7 @@ import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@@ -225,4 +226,84 @@ public abstract class FetchTask<T> extends Task<T> {
|
||||
NOT_CHECK_E_TAG,
|
||||
CACHED
|
||||
}
|
||||
|
||||
protected class DownloadState {
|
||||
private final int startPosition;
|
||||
private final int endPosition;
|
||||
private final int currentPosition;
|
||||
private final boolean finished;
|
||||
|
||||
public DownloadState(int startPosition, int endPosition, int currentPosition) {
|
||||
if (currentPosition < startPosition || currentPosition > endPosition) {
|
||||
throw new IllegalArgumentException("Illegal download state: start " + startPosition + ", end " + endPosition + ", cur " + currentPosition);
|
||||
}
|
||||
this.startPosition = startPosition;
|
||||
this.endPosition = endPosition;
|
||||
this.currentPosition = currentPosition;
|
||||
finished = currentPosition == endPosition;
|
||||
}
|
||||
|
||||
public int getStartPosition() {
|
||||
return startPosition;
|
||||
}
|
||||
|
||||
public int getEndPosition() {
|
||||
return endPosition;
|
||||
}
|
||||
|
||||
public int getCurrentPosition() {
|
||||
return currentPosition;
|
||||
}
|
||||
|
||||
public boolean isFinished() {
|
||||
return finished;
|
||||
}
|
||||
}
|
||||
|
||||
protected class DownloadMission {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static int downloadExecutorConcurrency = Math.min(Runtime.getRuntime().availableProcessors() * 4, 64);
|
||||
private static volatile ExecutorService DOWNLOAD_EXECUTOR;
|
||||
|
||||
/**
|
||||
* Get singleton instance of the thread pool for file downloading.
|
||||
*
|
||||
* @return Thread pool for FetchTask
|
||||
*/
|
||||
protected static ExecutorService download() {
|
||||
if (DOWNLOAD_EXECUTOR == null) {
|
||||
synchronized (Schedulers.class) {
|
||||
if (DOWNLOAD_EXECUTOR == null) {
|
||||
DOWNLOAD_EXECUTOR = new ThreadPoolExecutor(0, downloadExecutorConcurrency, 10, TimeUnit.SECONDS, new SynchronousQueue<>(),
|
||||
runnable -> {
|
||||
Thread thread = Executors.defaultThreadFactory().newThread(runnable);
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DOWNLOAD_EXECUTOR;
|
||||
}
|
||||
|
||||
public static void setDownloadExecutorConcurrency(int concurrency) {
|
||||
synchronized (Schedulers.class) {
|
||||
downloadExecutorConcurrency = concurrency;
|
||||
if (DOWNLOAD_EXECUTOR != null) {
|
||||
DOWNLOAD_EXECUTOR.shutdownNow();
|
||||
DOWNLOAD_EXECUTOR = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int getDownloadExecutorConcurrency() {
|
||||
synchronized (Schedulers.class) {
|
||||
return downloadExecutorConcurrency;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,27 +32,29 @@ public final class Schedulers {
|
||||
private Schedulers() {
|
||||
}
|
||||
|
||||
private static volatile ThreadPoolExecutor CACHED_EXECUTOR;
|
||||
|
||||
public static synchronized ThreadPoolExecutor newThread() {
|
||||
if (CACHED_EXECUTOR == null)
|
||||
CACHED_EXECUTOR = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
|
||||
60, TimeUnit.SECONDS, new SynchronousQueue<>(), Executors.defaultThreadFactory());
|
||||
|
||||
return CACHED_EXECUTOR;
|
||||
}
|
||||
|
||||
private static volatile ExecutorService IO_EXECUTOR;
|
||||
|
||||
public static synchronized ExecutorService io() {
|
||||
/**
|
||||
* Get singleton instance of the thread pool for I/O operations,
|
||||
* usually for reading files from disk, or Internet connections.
|
||||
*
|
||||
* This thread pool has no more than 4 threads, and number of threads will get
|
||||
* reduced if concurrency is less than thread number.
|
||||
*
|
||||
* @return Thread pool for I/O operations.
|
||||
*/
|
||||
public static ExecutorService io() {
|
||||
if (IO_EXECUTOR == null) {
|
||||
int threads = Math.min(Runtime.getRuntime().availableProcessors() * 4, 64);
|
||||
IO_EXECUTOR = Executors.newFixedThreadPool(threads,
|
||||
runnable -> {
|
||||
Thread thread = Executors.defaultThreadFactory().newThread(runnable);
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
});
|
||||
synchronized (Schedulers.class) {
|
||||
if (IO_EXECUTOR == null) {
|
||||
IO_EXECUTOR = new ThreadPoolExecutor(0, 4, 10, TimeUnit.SECONDS, new SynchronousQueue<>(),
|
||||
runnable -> {
|
||||
Thread thread = Executors.defaultThreadFactory().newThread(runnable);
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return IO_EXECUTOR;
|
||||
@@ -67,7 +69,7 @@ public final class Schedulers {
|
||||
}
|
||||
|
||||
public static Executor defaultScheduler() {
|
||||
return newThread();
|
||||
return ForkJoinPool.commonPool();
|
||||
}
|
||||
|
||||
public static synchronized void shutdown() {
|
||||
@@ -77,16 +79,8 @@ public final class Schedulers {
|
||||
// So when we want to close the app, no threads need to be waited for finish.
|
||||
// Sometimes it resolves the problem that the app does not exit.
|
||||
|
||||
if (CACHED_EXECUTOR != null)
|
||||
CACHED_EXECUTOR.shutdownNow();
|
||||
|
||||
if (IO_EXECUTOR != null)
|
||||
IO_EXECUTOR.shutdownNow();
|
||||
}
|
||||
|
||||
public static Future<?> schedule(Executor executor, Runnable command) {
|
||||
FutureTask<?> future = new FutureTask<Void>(command, null);
|
||||
executor.execute(future);
|
||||
return future;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user