feat: initial multithreaded downloading
This commit is contained in:
@@ -109,7 +109,7 @@ public final class LauncherHelper {
|
|||||||
try {
|
try {
|
||||||
checkGameState(profile, setting, version, () -> {
|
checkGameState(profile, setting, version, () -> {
|
||||||
Controllers.dialog(launchingStepsPane);
|
Controllers.dialog(launchingStepsPane);
|
||||||
Schedulers.newThread().execute(this::launch0);
|
Schedulers.defaultScheduler().execute(this::launch0);
|
||||||
});
|
});
|
||||||
} catch (InterruptedException | RejectedExecutionException ignore) {
|
} catch (InterruptedException | RejectedExecutionException ignore) {
|
||||||
}
|
}
|
||||||
@@ -199,7 +199,7 @@ public final class LauncherHelper {
|
|||||||
Controllers.dialog(i18n("version.launch_script.success", scriptFile.getAbsolutePath()));
|
Controllers.dialog(i18n("version.launch_script.success", scriptFile.getAbsolutePath()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}).thenRunAsync(Schedulers.defaultScheduler(), () -> {
|
}).thenRunAsync(() -> {
|
||||||
launchingLatch.await();
|
launchingLatch.await();
|
||||||
}).withStage("launch.state.waiting_launching"))
|
}).withStage("launch.state.waiting_launching"))
|
||||||
.withStagesHint(Lang.immutableListOf(
|
.withStagesHint(Lang.immutableListOf(
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import java.net.URL;
|
|||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
@@ -225,4 +226,84 @@ public abstract class FetchTask<T> extends Task<T> {
|
|||||||
NOT_CHECK_E_TAG,
|
NOT_CHECK_E_TAG,
|
||||||
CACHED
|
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,28 +32,30 @@ public final class Schedulers {
|
|||||||
private 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;
|
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) {
|
if (IO_EXECUTOR == null) {
|
||||||
int threads = Math.min(Runtime.getRuntime().availableProcessors() * 4, 64);
|
synchronized (Schedulers.class) {
|
||||||
IO_EXECUTOR = Executors.newFixedThreadPool(threads,
|
if (IO_EXECUTOR == null) {
|
||||||
|
IO_EXECUTOR = new ThreadPoolExecutor(0, 4, 10, TimeUnit.SECONDS, new SynchronousQueue<>(),
|
||||||
runnable -> {
|
runnable -> {
|
||||||
Thread thread = Executors.defaultThreadFactory().newThread(runnable);
|
Thread thread = Executors.defaultThreadFactory().newThread(runnable);
|
||||||
thread.setDaemon(true);
|
thread.setDaemon(true);
|
||||||
return thread;
|
return thread;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return IO_EXECUTOR;
|
return IO_EXECUTOR;
|
||||||
}
|
}
|
||||||
@@ -67,7 +69,7 @@ public final class Schedulers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Executor defaultScheduler() {
|
public static Executor defaultScheduler() {
|
||||||
return newThread();
|
return ForkJoinPool.commonPool();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static synchronized void shutdown() {
|
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.
|
// 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.
|
// Sometimes it resolves the problem that the app does not exit.
|
||||||
|
|
||||||
if (CACHED_EXECUTOR != null)
|
|
||||||
CACHED_EXECUTOR.shutdownNow();
|
|
||||||
|
|
||||||
if (IO_EXECUTOR != null)
|
if (IO_EXECUTOR != null)
|
||||||
IO_EXECUTOR.shutdownNow();
|
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