From 8c5e0db28e31d9e9366f54f6281b3cd449eae100 Mon Sep 17 00:00:00 2001 From: nast1415 Date: Mon, 15 Feb 2016 21:09:30 +0300 Subject: [PATCH 1/3] first attempt --- Java_4sem.iml | 21 ++++++ pom.xml | 78 +++++++++++++++++++++ src/main/java/ru/spbau/mit/Lazy.java | 5 ++ src/main/java/ru/spbau/mit/LazyFactory.java | 66 +++++++++++++++++ 4 files changed, 170 insertions(+) create mode 100644 Java_4sem.iml create mode 100644 pom.xml create mode 100644 src/main/java/ru/spbau/mit/Lazy.java create mode 100644 src/main/java/ru/spbau/mit/LazyFactory.java diff --git a/Java_4sem.iml b/Java_4sem.iml new file mode 100644 index 0000000..22f739f --- /dev/null +++ b/Java_4sem.iml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..2319589 --- /dev/null +++ b/pom.xml @@ -0,0 +1,78 @@ + + 4.0.0 + + ru.spbau.mit + assignments + 1.0-SNAPSHOT + jar + + assignments + http://maven.apache.org + + + UTF-8 + + + + + junit + junit + 4.12 + test + + + org.hamcrest + hamcrest-all + 1.3 + + + junit-addons + junit-addons + 1.4 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.3 + + 1.7 + 1.7 + + -Xlint:all + + + + + org.jacoco + jacoco-maven-plugin + 0.7.5.201505241946 + + + default-prepare-agent + + prepare-agent + + + + deafult-report + test + + report + + + + deafult-check + + check + + + + + + + \ No newline at end of file diff --git a/src/main/java/ru/spbau/mit/Lazy.java b/src/main/java/ru/spbau/mit/Lazy.java new file mode 100644 index 0000000..66428c4 --- /dev/null +++ b/src/main/java/ru/spbau/mit/Lazy.java @@ -0,0 +1,5 @@ +package ru.spbau.mit; + +public interface Lazy { + T get(); +} \ No newline at end of file diff --git a/src/main/java/ru/spbau/mit/LazyFactory.java b/src/main/java/ru/spbau/mit/LazyFactory.java new file mode 100644 index 0000000..b2f568d --- /dev/null +++ b/src/main/java/ru/spbau/mit/LazyFactory.java @@ -0,0 +1,66 @@ +package ru.spbau.mit; + +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; +import java.util.function.Supplier; + +public class LazyFactory { + public static Lazy createLazyOneThread(final Supplier supplier) { + return new Lazy() { + private boolean isCalled = false; + private T result; + + public T get() { + if (!isCalled) { + result = supplier.get(); + isCalled = true; + } + return result; + } + }; + } + + public static Lazy createLazyMultiThread(final Supplier supplier) { + return new Lazy() { + private volatile boolean isCalled = false; + private volatile T result; + + public T get() { + if (!isCalled) { + synchronized (this) { + if (!isCalled) { + result = supplier.get(); + isCalled = true; + } + } + } + return result; + } + }; + } + + private static class LockFreeLazy implements Lazy { + private volatile T result; + private volatile Supplier mySupplier; + private static final AtomicReferenceFieldUpdater updater = + AtomicReferenceFieldUpdater.newUpdater(LockFreeLazy.class, Object.class, "result"); + + public LockFreeLazy(Supplier supplier) { + mySupplier = supplier; + } + + + public T get() { + Supplier supplier = mySupplier; + if (supplier != null) { + if (updater.compareAndSet(this, null, supplier.get())) { + mySupplier = null; + } + } + return result; + } + } + + public static Lazy createLazyMultiThreadLockFree(Supplier supplier) { + return new LockFreeLazy(supplier); + } +} From 231717db17af7c9dff4315f5375baaa069581f84 Mon Sep 17 00:00:00 2001 From: nast1415 Date: Mon, 15 Feb 2016 22:27:24 +0300 Subject: [PATCH 2/3] tests added --- .../java/ru/spbau/mit/LazyFactoryTest.java | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 src/test/java/ru/spbau/mit/LazyFactoryTest.java diff --git a/src/test/java/ru/spbau/mit/LazyFactoryTest.java b/src/test/java/ru/spbau/mit/LazyFactoryTest.java new file mode 100644 index 0000000..eea05f6 --- /dev/null +++ b/src/test/java/ru/spbau/mit/LazyFactoryTest.java @@ -0,0 +1,149 @@ +package ru.spbau.mit; + +import org.junit.Test; + +import java.util.Random; +import java.util.function.Supplier; + +import static org.junit.Assert.*; + +public class LazyFactoryTest { + + //Create suppliers + + private Supplier nullSupplier = new Supplier() { + private boolean isCalled = false; + + @Override + public String get() { + assertFalse(isCalled); + isCalled = true; + return null; + } + }; + + private Supplier supplier = new Supplier() { + private boolean isCalled = false; + + @Override + public String get() { + assertFalse(isCalled); + isCalled = true; + return "I've done!"; + } + }; + + //Special functions for tests + + public void checkForNullTests(Lazy lazy) { + assertNull(lazy.get()); + assertNull(lazy.get()); + } + + public void checkForEqualTests(Lazy lazy) { + String firstGet = lazy.get(); + String secondGet = lazy.get(); + assertEquals(firstGet, secondGet); + assertEquals(firstGet, "I've done!"); + } + + public Supplier createMultiThreadSupplier(final Random random) { + return new Supplier() { + @Override + public Integer get() { + return random.nextInt(500); + } + }; + } + + public void createAndCheckThreads(final Lazy lazy) { + Thread[] threads = new Thread[5]; + final Integer[] result = new Integer[5]; + + for (int i = 0; i < 5; i++) { + final int currentNumber = i; + threads[currentNumber] = new Thread(new Runnable() { + @Override + public void run() { + result[currentNumber] = lazy.get(); + } + }); + } + + for (int i = 0; i < 5; i++) { + threads[i].start(); + } + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + for (int i = 0; i < 4; i++) { + assertEquals(result[i], result[i + 1]); + } + } + + //Tests for one thread + + @Test + public void nullTestLazyForOneThread() { + Lazy myLazy = LazyFactory.createLazyOneThread(nullSupplier); + checkForNullTests(myLazy); + } + + @Test + public void equalsTestLazyForOneThread() { + Lazy myLazy = LazyFactory.createLazyOneThread(supplier); + checkForEqualTests(myLazy); + } + + //MultiThread tests + + @Test + public void nullTestLazyMultiThread() { + Lazy myLazy = LazyFactory.createLazyMultiThread(nullSupplier); + checkForNullTests(myLazy); + } + + @Test + public void equalsTestLazyMultiThread() { + Lazy myLazy = LazyFactory.createLazyMultiThread(supplier); + checkForEqualTests(myLazy); + } + + @Test + public void dataRaceTestLazyMultiThread() { + final Random myRandom = new Random(); + Supplier mySupplier = createMultiThreadSupplier(myRandom); + Lazy myLazy = LazyFactory.createLazyMultiThread(mySupplier); + + createAndCheckThreads(myLazy); + + } + + //MultiThread lock-free tests + + @Test + public void nullTestLazyMultiThreadLockFree() { + Lazy myLazy = LazyFactory.createLazyMultiThreadLockFree(nullSupplier); + checkForNullTests(myLazy); + } + + @Test + public void equalsTestLazyMultiThreadLockFree() { + Lazy myLazy = LazyFactory.createLazyMultiThreadLockFree(supplier); + checkForEqualTests(myLazy); + } + + @Test + public void dataRaceTestLazyMultiThreadLockFree() { + final Random myRandom = new Random(); + Supplier mySupplier = createMultiThreadSupplier(myRandom); + Lazy myLazy = LazyFactory.createLazyMultiThreadLockFree(mySupplier); + + createAndCheckThreads(myLazy); + + } +} From 0c6b1458122fa4b860547d9684fd6dd5ade879f5 Mon Sep 17 00:00:00 2001 From: nast1415 Date: Tue, 1 Mar 2016 08:52:21 +0300 Subject: [PATCH 3/3] modified (modified LazyFactory, add laziness test, modified dataRace tests) --- .../ru/spbau/mit/FactoryFromSupplier.java | 7 + src/main/java/ru/spbau/mit/LazyFactory.java | 14 +- .../java/ru/spbau/mit/LazyFactoryTest.java | 138 ++++++++++++------ 3 files changed, 106 insertions(+), 53 deletions(-) create mode 100644 src/main/java/ru/spbau/mit/FactoryFromSupplier.java diff --git a/src/main/java/ru/spbau/mit/FactoryFromSupplier.java b/src/main/java/ru/spbau/mit/FactoryFromSupplier.java new file mode 100644 index 0000000..a8a704a --- /dev/null +++ b/src/main/java/ru/spbau/mit/FactoryFromSupplier.java @@ -0,0 +1,7 @@ +package ru.spbau.mit; + +import java.util.function.Supplier; + +public interface FactoryFromSupplier { + Lazy createLazy(Supplier supplier); +} diff --git a/src/main/java/ru/spbau/mit/LazyFactory.java b/src/main/java/ru/spbau/mit/LazyFactory.java index b2f568d..73a93cb 100644 --- a/src/main/java/ru/spbau/mit/LazyFactory.java +++ b/src/main/java/ru/spbau/mit/LazyFactory.java @@ -40,20 +40,20 @@ public T get() { private static class LockFreeLazy implements Lazy { private volatile T result; - private volatile Supplier mySupplier; + private volatile Supplier supplier; private static final AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(LockFreeLazy.class, Object.class, "result"); - public LockFreeLazy(Supplier supplier) { - mySupplier = supplier; + public LockFreeLazy(Supplier supp) { + supplier = supp; } public T get() { - Supplier supplier = mySupplier; - if (supplier != null) { - if (updater.compareAndSet(this, null, supplier.get())) { - mySupplier = null; + Supplier supp = supplier; + if (supp != null) { + if (updater.compareAndSet(this, null, supp.get())) { + supplier = null; } } return result; diff --git a/src/test/java/ru/spbau/mit/LazyFactoryTest.java b/src/test/java/ru/spbau/mit/LazyFactoryTest.java index eea05f6..e4f76c9 100644 --- a/src/test/java/ru/spbau/mit/LazyFactoryTest.java +++ b/src/test/java/ru/spbau/mit/LazyFactoryTest.java @@ -2,16 +2,20 @@ import org.junit.Test; -import java.util.Random; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; import static org.junit.Assert.*; public class LazyFactoryTest { + private static final int NUMBER_OF_THREADS = 50; + //Create suppliers - private Supplier nullSupplier = new Supplier() { + private final Supplier nullSupplier = new Supplier() { private boolean isCalled = false; @Override @@ -22,7 +26,7 @@ public String get() { } }; - private Supplier supplier = new Supplier() { + private final Supplier supplier = new Supplier() { private boolean isCalled = false; @Override @@ -35,19 +39,19 @@ public String get() { //Special functions for tests - public void checkForNullTests(Lazy lazy) { + private void checkForNullTests(Lazy lazy) { assertNull(lazy.get()); assertNull(lazy.get()); } - public void checkForEqualTests(Lazy lazy) { + private void checkForEqualTests(Lazy lazy) { String firstGet = lazy.get(); String secondGet = lazy.get(); assertEquals(firstGet, secondGet); assertEquals(firstGet, "I've done!"); } - public Supplier createMultiThreadSupplier(final Random random) { + private Supplier createMultiThreadSupplier(final Random random) { return new Supplier() { @Override public Integer get() { @@ -56,79 +60,126 @@ public Integer get() { }; } - public void createAndCheckThreads(final Lazy lazy) { - Thread[] threads = new Thread[5]; - final Integer[] result = new Integer[5]; + private static class SupplCounter implements Supplier { + private AtomicInteger cnt = new AtomicInteger(0); + private Object result; + + SupplCounter(Object result) { + this.result = result; + } + + @Override + public Object get() { + Thread.yield(); + cnt.incrementAndGet(); + return result; + } + + public Integer getCount() { + return cnt.get(); + } + } - for (int i = 0; i < 5; i++) { - final int currentNumber = i; - threads[currentNumber] = new Thread(new Runnable() { - @Override - public void run() { - result[currentNumber] = lazy.get(); + private void checkForMultiThread(FactoryFromSupplier factory, boolean isSupplierCalledOnce, + Object returnValue) { + SupplCounter supplier = new SupplCounter(returnValue); + Lazy lazy = factory.createLazy(supplier); + + List tasks = new ArrayList<>(); + List results = Collections.synchronizedList(new ArrayList<>()); + CyclicBarrier barrier = new CyclicBarrier(NUMBER_OF_THREADS); + + for (int i = 0; i < NUMBER_OF_THREADS; i++) { + Thread task = new Thread(() -> { + try { + barrier.await(); + } catch (Exception e) { + e.printStackTrace(); } + results.add(lazy.get()); }); + tasks.add(task); + task.start(); } - for (int i = 0; i < 5; i++) { - threads[i].start(); + for (Thread task : tasks) { + try { + task.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } } - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); + assertEquals(NUMBER_OF_THREADS, results.size()); + + for (Object result : results) { + assertTrue(returnValue == result); } - for (int i = 0; i < 4; i++) { - assertEquals(result[i], result[i + 1]); + assertTrue(supplier.getCount() > 0); + if (isSupplierCalledOnce) { + assertTrue(1 == supplier.getCount()); } } //Tests for one thread - @Test public void nullTestLazyForOneThread() { - Lazy myLazy = LazyFactory.createLazyOneThread(nullSupplier); - checkForNullTests(myLazy); + Lazy lazy = LazyFactory.createLazyOneThread(nullSupplier); + checkForNullTests(lazy); } @Test public void equalsTestLazyForOneThread() { - Lazy myLazy = LazyFactory.createLazyOneThread(supplier); - checkForEqualTests(myLazy); + Lazy lazy = LazyFactory.createLazyOneThread(supplier); + checkForEqualTests(lazy); + } + + private static class classWrapper { + public volatile T content; + + public classWrapper(T content) { + this.content = content; + } + } + + @Test + public void lazinessTestLazyForOneThread() { + classWrapper isAsked = new classWrapper<>(false); + + Lazy lazy = LazyFactory.createLazyOneThread(() -> { + assertTrue(isAsked.content); + return "It's OK!"; + }); + + isAsked.content = true; } //MultiThread tests @Test public void nullTestLazyMultiThread() { - Lazy myLazy = LazyFactory.createLazyMultiThread(nullSupplier); - checkForNullTests(myLazy); + Lazy lazy = LazyFactory.createLazyMultiThread(nullSupplier); + checkForNullTests(lazy); } @Test public void equalsTestLazyMultiThread() { - Lazy myLazy = LazyFactory.createLazyMultiThread(supplier); - checkForEqualTests(myLazy); + Lazy lazy = LazyFactory.createLazyMultiThread(supplier); + checkForEqualTests(lazy); } @Test public void dataRaceTestLazyMultiThread() { - final Random myRandom = new Random(); - Supplier mySupplier = createMultiThreadSupplier(myRandom); - Lazy myLazy = LazyFactory.createLazyMultiThread(mySupplier); - - createAndCheckThreads(myLazy); - + checkForMultiThread(LazyFactory::createLazyMultiThread, true, new Object()); } //MultiThread lock-free tests @Test public void nullTestLazyMultiThreadLockFree() { - Lazy myLazy = LazyFactory.createLazyMultiThreadLockFree(nullSupplier); - checkForNullTests(myLazy); + Lazy lazy = LazyFactory.createLazyMultiThreadLockFree(nullSupplier); + checkForNullTests(lazy); } @Test @@ -139,11 +190,6 @@ public void equalsTestLazyMultiThreadLockFree() { @Test public void dataRaceTestLazyMultiThreadLockFree() { - final Random myRandom = new Random(); - Supplier mySupplier = createMultiThreadSupplier(myRandom); - Lazy myLazy = LazyFactory.createLazyMultiThreadLockFree(mySupplier); - - createAndCheckThreads(myLazy); - + checkForMultiThread(LazyFactory::createLazyMultiThreadLockFree, false, new Object()); } }