从 synchronized 切换至 ReentrantLock (#4564)
This commit is contained in:
@@ -58,6 +58,7 @@ import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.stream.Collectors;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
@@ -706,6 +707,7 @@ public final class LauncherHelper {
|
||||
*/
|
||||
private final class HMCLProcessListener implements ProcessListener {
|
||||
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
private final HMCLGameRepository repository;
|
||||
private final Version version;
|
||||
private final LaunchOptions launchOptions;
|
||||
@@ -849,21 +851,27 @@ public final class LauncherHelper {
|
||||
level = Lang.requireNonNullElse(Log4jLevel.guessLevel(log), Log4jLevel.INFO);
|
||||
logBuffer.add(new Log(log, level));
|
||||
} else {
|
||||
synchronized (this) {
|
||||
lock.lock();
|
||||
try {
|
||||
logs.addLast(new Log(log, level));
|
||||
if (logs.size() > Log.getLogLines())
|
||||
logs.removeFirst();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
if (!lwjgl) {
|
||||
String lowerCaseLog = log.toLowerCase(Locale.ROOT);
|
||||
if (!detectWindow || lowerCaseLog.contains("lwjgl version") || lowerCaseLog.contains("lwjgl openal")) {
|
||||
synchronized (this) {
|
||||
lock.lock();
|
||||
try {
|
||||
if (!lwjgl) {
|
||||
lwjgl = true;
|
||||
finishLaunch();
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -888,9 +896,12 @@ public final class LauncherHelper {
|
||||
|
||||
// Game crashed before opening the game window.
|
||||
if (!lwjgl) {
|
||||
synchronized (this) {
|
||||
lock.lock();
|
||||
try {
|
||||
if (!lwjgl)
|
||||
finishLaunch();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -287,7 +287,7 @@ public class PartialInflaterInputStream extends FilterInputStream {
|
||||
* the mark position becomes invalid.
|
||||
* @see InputStream#reset()
|
||||
*/
|
||||
public synchronized void mark(int readlimit) {
|
||||
public void mark(int readlimit) {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -302,7 +302,7 @@ public class PartialInflaterInputStream extends FilterInputStream {
|
||||
* @see InputStream#mark(int)
|
||||
* @see IOException
|
||||
*/
|
||||
public synchronized void reset() throws IOException {
|
||||
public void reset() throws IOException {
|
||||
throw new IOException("mark/reset not supported");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ public final class Schedulers {
|
||||
return ForkJoinPool.commonPool();
|
||||
}
|
||||
|
||||
public static synchronized void shutdown() {
|
||||
public static void shutdown() {
|
||||
LOG.info("Shutting down executor services.");
|
||||
|
||||
// shutdownNow will interrupt all threads.
|
||||
|
||||
@@ -25,6 +25,7 @@ import javafx.beans.value.ObservableValue;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -144,6 +145,7 @@ public abstract class BindingMapping<T, U> extends ObjectBinding<U> {
|
||||
|
||||
private static final class AsyncMappedBinding<T, U> extends BindingMapping<T, U> {
|
||||
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
private boolean initialized = false;
|
||||
private T prev;
|
||||
private U value;
|
||||
@@ -159,13 +161,16 @@ public abstract class BindingMapping<T, U> extends ObjectBinding<U> {
|
||||
}
|
||||
|
||||
private void tryUpdateValue(T currentPrev) {
|
||||
synchronized (this) {
|
||||
lock.lock();
|
||||
try {
|
||||
if ((initialized && Objects.equals(prev, currentPrev))
|
||||
|| isComputing(currentPrev)) {
|
||||
return;
|
||||
}
|
||||
computing = true;
|
||||
computingPrev = currentPrev;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
CompletableFuture<? extends U> task;
|
||||
@@ -189,7 +194,8 @@ public abstract class BindingMapping<T, U> extends ObjectBinding<U> {
|
||||
}
|
||||
|
||||
private void valueUpdate(T currentPrev, U computed) {
|
||||
synchronized (this) {
|
||||
lock.lock();
|
||||
try {
|
||||
if (isComputing(currentPrev)) {
|
||||
computing = false;
|
||||
computingPrev = null;
|
||||
@@ -197,15 +203,20 @@ public abstract class BindingMapping<T, U> extends ObjectBinding<U> {
|
||||
value = computed;
|
||||
initialized = true;
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void valueUpdateFailed(T currentPrev) {
|
||||
synchronized (this) {
|
||||
lock.lock();
|
||||
try {
|
||||
if (isComputing(currentPrev)) {
|
||||
computing = false;
|
||||
computingPrev = null;
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
||||
@@ -37,6 +38,7 @@ import javafx.beans.binding.ObjectBinding;
|
||||
*/
|
||||
public final class ObservableCache<K, V, E extends Exception> {
|
||||
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
private final ExceptionalFunction<K, V, E> source;
|
||||
private final BiConsumer<K, Throwable> exceptionHandler;
|
||||
private final V fallbackValue;
|
||||
@@ -54,22 +56,29 @@ public final class ObservableCache<K, V, E extends Exception> {
|
||||
}
|
||||
|
||||
public Optional<V> getImmediately(K key) {
|
||||
synchronized (this) {
|
||||
lock.lock();
|
||||
try {
|
||||
return Optional.ofNullable(cache.get(key));
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void put(K key, V value) {
|
||||
synchronized (this) {
|
||||
lock.lock();
|
||||
try {
|
||||
cache.put(key, value);
|
||||
invalidated.remove(key);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
Platform.runLater(observable::invalidate);
|
||||
}
|
||||
|
||||
private CompletableFuture<V> query(K key, Executor executor) {
|
||||
CompletableFuture<V> future;
|
||||
synchronized (this) {
|
||||
lock.lock();
|
||||
try {
|
||||
CompletableFuture<V> prev = pendings.get(key);
|
||||
if (prev != null) {
|
||||
return prev;
|
||||
@@ -77,6 +86,8 @@ public final class ObservableCache<K, V, E extends Exception> {
|
||||
future = new CompletableFuture<>();
|
||||
pendings.put(key, future);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
executor.execute(() -> {
|
||||
@@ -84,18 +95,24 @@ public final class ObservableCache<K, V, E extends Exception> {
|
||||
try {
|
||||
result = source.apply(key);
|
||||
} catch (Throwable ex) {
|
||||
synchronized (this) {
|
||||
lock.lock();
|
||||
try {
|
||||
pendings.remove(key);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
exceptionHandler.accept(key, ex);
|
||||
future.completeExceptionally(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
lock.lock();
|
||||
try {
|
||||
cache.put(key, result);
|
||||
invalidated.remove(key);
|
||||
pendings.remove(key, future);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
future.complete(result);
|
||||
Platform.runLater(observable::invalidate);
|
||||
@@ -106,11 +123,14 @@ public final class ObservableCache<K, V, E extends Exception> {
|
||||
|
||||
public V get(K key) {
|
||||
V cached;
|
||||
synchronized (this) {
|
||||
lock.lock();
|
||||
try {
|
||||
cached = cache.get(key);
|
||||
if (cached != null && !invalidated.containsKey(key)) {
|
||||
return cached;
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -143,7 +163,9 @@ public final class ObservableCache<K, V, E extends Exception> {
|
||||
return Bindings.createObjectBinding(() -> {
|
||||
V result;
|
||||
boolean refresh;
|
||||
synchronized (this) {
|
||||
|
||||
lock.lock();
|
||||
try {
|
||||
result = cache.get(key);
|
||||
if (result == null) {
|
||||
result = fallbackValue;
|
||||
@@ -151,6 +173,8 @@ public final class ObservableCache<K, V, E extends Exception> {
|
||||
} else {
|
||||
refresh = invalidated.containsKey(key);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
if (!quiet && refresh) {
|
||||
query(key, executor);
|
||||
@@ -160,10 +184,13 @@ public final class ObservableCache<K, V, E extends Exception> {
|
||||
}
|
||||
|
||||
public void invalidate(K key) {
|
||||
synchronized (this) {
|
||||
lock.lock();
|
||||
try {
|
||||
if (cache.containsKey(key)) {
|
||||
invalidated.put(key, Boolean.TRUE);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
Platform.runLater(observable::invalidate);
|
||||
}
|
||||
|
||||
@@ -22,17 +22,17 @@ import org.jackhuang.hmcl.util.Lang;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* The managed process.
|
||||
*
|
||||
* @author huangyuhui
|
||||
* @see org.jackhuang.hmcl.launch.ExitWaiter
|
||||
* @see org.jackhuang.hmcl.launch.StreamPump
|
||||
*/
|
||||
/// The managed process.
|
||||
///
|
||||
/// @author huangyuhui
|
||||
/// <!-- @see org.jackhuang.hmcl.launch.ExitWaiter -->
|
||||
/// @see org.jackhuang.hmcl.launch.StreamPump
|
||||
public final class ManagedProcess {
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
private final Process process;
|
||||
private final List<String> commands;
|
||||
private final String classpath;
|
||||
@@ -54,7 +54,7 @@ public final class ManagedProcess {
|
||||
*/
|
||||
public ManagedProcess(Process process, List<String> commands) {
|
||||
this.process = process;
|
||||
this.commands = Collections.unmodifiableList(new ArrayList<>(commands));
|
||||
this.commands = List.copyOf(commands);
|
||||
this.classpath = null;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ public final class ManagedProcess {
|
||||
*/
|
||||
public ManagedProcess(Process process, List<String> commands, String classpath) {
|
||||
this.process = process;
|
||||
this.commands = Collections.unmodifiableList(new ArrayList<>(commands));
|
||||
this.commands = List.copyOf(commands);
|
||||
this.classpath = classpath;
|
||||
}
|
||||
|
||||
@@ -111,9 +111,11 @@ public final class ManagedProcess {
|
||||
*
|
||||
* @see #addLine
|
||||
*/
|
||||
public synchronized List<String> getLines(Predicate<String> lineFilter) {
|
||||
public List<String> getLines(Predicate<String> lineFilter) {
|
||||
lock.lock();
|
||||
|
||||
if (lineFilter == null)
|
||||
return Collections.unmodifiableList(Arrays.asList(lines.toArray(new String[0])));
|
||||
return List.copyOf(lines);
|
||||
|
||||
ArrayList<String> res = new ArrayList<>();
|
||||
for (String line : this.lines) {
|
||||
@@ -123,8 +125,13 @@ public final class ManagedProcess {
|
||||
return Collections.unmodifiableList(res);
|
||||
}
|
||||
|
||||
public synchronized void addLine(String line) {
|
||||
lines.add(line);
|
||||
public void addLine(String line) {
|
||||
lock.lock();
|
||||
try {
|
||||
lines.add(line);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,15 +140,20 @@ public final class ManagedProcess {
|
||||
* If a thread is monitoring this raw process,
|
||||
* you are required to add the instance by this method.
|
||||
*/
|
||||
public synchronized void addRelatedThread(Thread thread) {
|
||||
relatedThreads.add(thread);
|
||||
public void addRelatedThread(Thread thread) {
|
||||
lock.lock();
|
||||
try {
|
||||
relatedThreads.add(thread);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void pumpInputStream(Consumer<String> onLogLine) {
|
||||
public void pumpInputStream(Consumer<String> onLogLine) {
|
||||
addRelatedThread(Lang.thread(new StreamPump(process.getInputStream(), onLogLine, OperatingSystem.NATIVE_CHARSET), "ProcessInputStreamPump", true));
|
||||
}
|
||||
|
||||
public synchronized void pumpErrorStream(Consumer<String> onLogLine) {
|
||||
public void pumpErrorStream(Consumer<String> onLogLine) {
|
||||
addRelatedThread(Lang.thread(new StreamPump(process.getErrorStream(), onLogLine, OperatingSystem.NATIVE_CHARSET), "ProcessErrorStreamPump", true));
|
||||
}
|
||||
|
||||
@@ -172,8 +184,13 @@ public final class ManagedProcess {
|
||||
destroyRelatedThreads();
|
||||
}
|
||||
|
||||
public synchronized void destroyRelatedThreads() {
|
||||
relatedThreads.forEach(Thread::interrupt);
|
||||
public void destroyRelatedThreads() {
|
||||
lock.lock();
|
||||
try {
|
||||
relatedThreads.forEach(Thread::interrupt);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user