package ch.claude_martin.enums.proxy;
import static java.util.Objects.requireNonNull;
import java.lang.reflect.Proxy;
import java.util.Collections;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Allows to make any object thread-safe. This works similar to
* {@link Collections#synchronizedCollection(java.util.Collection)}. But this
* uses a proxy, which will synchronize on the object.
*
* <p>
* The object must implement some interface so that the returned proxy can be
* used as an implementation of that interface.
*
* <p>
* Note: This does not make the elements within the object thread-safe. This
* only works if the object hides all information and no other objects share
* references to the inner data structures.
*
* @author Claude Martin
*/
public class SynchronizedObject {
/**
* Returns a proxy instance which will synchronize all calls to the object.
*
* @param object
* The object which will be used for synchronization and to redirect
* all method invocations.
* @param type
* The interface type of the returned proxy. Both the object and the
* proxy will share this interface.
* @return A new tread-safe proxy.
*/
@SuppressWarnings("unchecked")
public static <I, T extends I> I synchronize(final T object, final Class<I> type) {
return (I) Proxy.newProxyInstance(//
requireNonNull(object).getClass().getClassLoader(),//
new Class[] { requireNonNull(type) },//
(proxy, method, args) -> {
synchronized (object) {
return method.invoke(object, args);
}
});
}
/**
* Returns a proxy instance which will synchronize all calls to the object
* using a given lock.
*
* @param object
* The object which will be used for synchronization and to redirect
* all method invocations.
* @param type
* The interface type of the returned proxy. Both the object and the
* proxy will share this interface.
* @param lock
* A lock object used to obtain a lock.
* @return A new tread-safe proxy.
*/
@SuppressWarnings("unchecked")
public static <I, T extends I> I synchronize(final T object, final Class<I> type, final Lock lock) {
return (I) Proxy.newProxyInstance(//
requireNonNull(object).getClass().getClassLoader(),//
new Class[] { requireNonNull(type) },//
(proxy, method, args) -> {
lock.lock();
try {
return method.invoke(object, args);
} finally {
lock.unlock();
}
});
}
}
-----------------------------
-----------------------------
(The rest is just for testing)
public final class SynchronizedObjectTest {
public static void main(final String[] args) throws InterruptedException {
final ICounter counter = synchronize(new Counter(), ICounter.class);
final ExecutorService pool = Executors.newCachedThreadPool();
for (int i = 0; i < 10000; i++)
pool.execute(() -> counter.increment());
pool.shutdown();
pool.awaitTermination(1, TimeUnit.MINUTES);
System.out.println(counter.get());
}
static interface ICounter {
public int get();
public void increment();
}
static class Counter implements ICounter {
int i = 0;
@Override
public int get() {
return this.i;
}
@Override
public void increment() {
this.i++;
}
}
}
Comments