feat: initial multithreaded downloading

This commit is contained in:
yuhuihuang
2020-08-22 21:33:10 +08:00
parent 8108f11c45
commit 4715a95a54
3 changed files with 104 additions and 29 deletions

View File

@@ -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(

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}