Skip to content

Instantly share code, notes, and snippets.

@StanSvec
Created June 7, 2016 10:37
Show Gist options
  • Save StanSvec/06c52d409f882158577b60718e26d661 to your computer and use it in GitHub Desktop.
Save StanSvec/06c52d409f882158577b60718e26d661 to your computer and use it in GitHub Desktop.
Allow monitor and manage shared instance
import java.util.Optional;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
/**
* This is a container for shared instance allowing monitoring of sharing and safe manipulation.
*
* A new instance is provided by the supplier when the container is created. Every execution of {@link Supplier#get()} must return new unique instance.
* The instance is provided to the borrowers by using {@link #share()} method. The number of borrowers is unlimited,
* but every execution of {@link #share()} method must be eventually followed by the execution of {@link #unshare(T)} method
* when the instance is no longer used by a borrower. After execution of {@link #unshare(T)} method the borrower must not
* manipulate with the instance and should not retain the reference on it.
* The instance is considered unshared when the number of execution of {@link #share()} and {@link #unshare(T)} methods is equal.
*
* The instance can be taken (removed) when is no longer shared and replaced with the new one by using {@link #takeSafely()}.
* The same supplier is used for obtaining the new instance. If this method is executed and the instance is still shared
* then it will wait until all borrowers will call {@link #unshare(T)} method or until timeout.
* However the new instance become available immediately and can be shared even when the previous instance is still shared.
*/
public class SharedInstance<T> {
private final Supplier<? extends T> supplier;
private final AtomicReference<ManagedInstance<T>> previous;
private volatile ManagedInstance<T> current;
public SharedInstance(Supplier<? extends T> supplier) {
this.supplier = supplier;
this.previous = new AtomicReference<>();
this.current = new ManagedInstance<>(supplier.get());
}
public T share() {
Optional<T> instOpt = current.get();
if (!instOpt.isPresent()) {
synchronized (this) {
instOpt = current.get();
}
if (!instOpt.isPresent()) {
throw new IllegalStateException("The shared instance is in the corrupted state because of illegal execution of unshare method");
}
}
return instOpt.get();
}
public boolean unshare(T instance) {
ManagedInstance<T> pObj = null;
ManagedInstance<T> prev = previous.get();
if ((prev != null) && prev.contains(instance)) {
pObj = prev;
} else if (current.contains(instance)) {
pObj = current;
}
if (pObj != null) {
pObj.release();
return true;
} else {
return false;
}
}
public synchronized Future<T> takeSafely() {
previous.set(current);
current = new ManagedInstance<>(supplier.get());
ManagedInstance<T> prev = previous.get();
return new FutureTask<T>(() -> prev.closeAndGetAfterAllReleased()) {
@Override protected void done() {
previous.compareAndSet(prev, null);
}
};
}
private static class ManagedInstance<T> {
private final T instance;
private final Phaser phaser;
public ManagedInstance(T instance) {
this.instance = instance;
this.phaser = new Phaser(1);
}
public Optional<T> get() {
if (phaser.register() != 0) {
return Optional.empty();
}
return Optional.of(instance);
}
public void release() {
phaser.arrive();
}
public T closeAndGetAfterAllReleased() {
phaser.arriveAndAwaitAdvance();
return instance;
}
public boolean contains(T instance) {
return (this.instance == instance);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment