diff --git a/core-java-modules/core-java-function/src/main/java/com/baeldung/lazylambda/LambdaSupplier.java b/core-java-modules/core-java-function/src/main/java/com/baeldung/lazylambda/LambdaSupplier.java new file mode 100644 index 000000000000..36cd1b31ae71 --- /dev/null +++ b/core-java-modules/core-java-function/src/main/java/com/baeldung/lazylambda/LambdaSupplier.java @@ -0,0 +1,16 @@ +package com.baeldung.lazylambda; + +import java.util.function.Supplier; + +public class LambdaSupplier { + + protected final Supplier expensiveData; + + public LambdaSupplier(Supplier expensiveData) { + this.expensiveData = expensiveData; + } + + public T getData() { + return expensiveData.get(); + } +} diff --git a/core-java-modules/core-java-function/src/main/java/com/baeldung/lazylambda/LazyLambdaSupplier.java b/core-java-modules/core-java-function/src/main/java/com/baeldung/lazylambda/LazyLambdaSupplier.java new file mode 100644 index 000000000000..dce3cc4997a9 --- /dev/null +++ b/core-java-modules/core-java-function/src/main/java/com/baeldung/lazylambda/LazyLambdaSupplier.java @@ -0,0 +1,21 @@ +package com.baeldung.lazylambda; + +import java.util.function.Supplier; + +public class LazyLambdaSupplier extends LambdaSupplier { + + private T data; + + public LazyLambdaSupplier(Supplier expensiveData) { + super(expensiveData); + } + + @Override + public T getData() { + if (data != null) { + return data; + } + return data = expensiveData.get(); + } + +} diff --git a/core-java-modules/core-java-function/src/main/java/com/baeldung/lazylambda/LazyLambdaThreadSafeSupplier.java b/core-java-modules/core-java-function/src/main/java/com/baeldung/lazylambda/LazyLambdaThreadSafeSupplier.java new file mode 100644 index 000000000000..32eec0a5d87e --- /dev/null +++ b/core-java-modules/core-java-function/src/main/java/com/baeldung/lazylambda/LazyLambdaThreadSafeSupplier.java @@ -0,0 +1,26 @@ +package com.baeldung.lazylambda; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; + +public class LazyLambdaThreadSafeSupplier extends LambdaSupplier { + + private final AtomicReference data; + + public LazyLambdaThreadSafeSupplier(Supplier expensiveData) { + super(expensiveData); + data = new AtomicReference<>(); + } + + public T getData() { + if (data.get() == null) { + synchronized (data) { + if (data.get() == null) { + data.set(expensiveData.get()); + } + } + } + return data.get(); + } + +} diff --git a/core-java-modules/core-java-function/src/test/java/com/baeldung/lazylambda/LambdaSupplierUnitTest.java b/core-java-modules/core-java-function/src/test/java/com/baeldung/lazylambda/LambdaSupplierUnitTest.java new file mode 100644 index 000000000000..f0a500e0840c --- /dev/null +++ b/core-java-modules/core-java-function/src/test/java/com/baeldung/lazylambda/LambdaSupplierUnitTest.java @@ -0,0 +1,24 @@ +package com.baeldung.lazylambda; + +import java.util.function.Supplier; + +import org.junit.Test; +import org.mockito.Mockito; + +public class LambdaSupplierUnitTest { + + @Test + public void whenCalledMultipleTimes_thenShouldBeCalledMultipleTimes() { + @SuppressWarnings("unchecked") Supplier mockedExpensiveFunction = Mockito.mock(Supplier.class); + Mockito.when(mockedExpensiveFunction.get()) + .thenReturn("expensive call"); + LambdaSupplier testee = new LambdaSupplier<>(mockedExpensiveFunction); + Mockito.verify(mockedExpensiveFunction, Mockito.never()) + .get(); + testee.getData(); + testee.getData(); + Mockito.verify(mockedExpensiveFunction, Mockito.times(2)) + .get(); + } + +} diff --git a/core-java-modules/core-java-function/src/test/java/com/baeldung/lazylambda/LazyLambdaSupplierUnitTest.java b/core-java-modules/core-java-function/src/test/java/com/baeldung/lazylambda/LazyLambdaSupplierUnitTest.java new file mode 100644 index 000000000000..1371983a3db4 --- /dev/null +++ b/core-java-modules/core-java-function/src/test/java/com/baeldung/lazylambda/LazyLambdaSupplierUnitTest.java @@ -0,0 +1,51 @@ +package com.baeldung.lazylambda; + +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.stubbing.Answer; + +public class LazyLambdaSupplierUnitTest { + + @Test + public void whenCalledMultipleTimes_thenShouldBeCalledOnlyOnce() { + @SuppressWarnings("unchecked") Supplier mockedExpensiveFunction = Mockito.mock(Supplier.class); + Mockito.when(mockedExpensiveFunction.get()) + .thenReturn("expensive call"); + LazyLambdaSupplier testee = new LazyLambdaSupplier<>(mockedExpensiveFunction); + Mockito.verify(mockedExpensiveFunction, Mockito.never()) + .get(); + testee.getData(); + testee.getData(); + Mockito.verify(mockedExpensiveFunction, Mockito.times(1)) + .get(); + } + + @Test + public void whenCalledMultipleTimesConcurrently_thenShouldBeCalledMultipleTimes() throws InterruptedException { + @SuppressWarnings("unchecked") Supplier mockedExpensiveFunction = Mockito.mock(Supplier.class); + Mockito.when(mockedExpensiveFunction.get()) + .thenAnswer((Answer) invocation -> { + Thread.sleep(1000L); + return "Late response!"; + }); + LazyLambdaSupplier testee = new LazyLambdaSupplier<>(mockedExpensiveFunction); + Mockito.verify(mockedExpensiveFunction, Mockito.never()) + .get(); + + ExecutorService executorService = Executors.newFixedThreadPool(4); + executorService.invokeAll(List.of(testee::getData, testee::getData)); + executorService.shutdown(); + if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) { + executorService.shutdownNow(); + } + + Mockito.verify(mockedExpensiveFunction, Mockito.times(2)) + .get(); + } +} diff --git a/core-java-modules/core-java-function/src/test/java/com/baeldung/lazylambda/LazyLambdaThreadSafeSupplierUnitTest.java b/core-java-modules/core-java-function/src/test/java/com/baeldung/lazylambda/LazyLambdaThreadSafeSupplierUnitTest.java new file mode 100644 index 000000000000..83bd49aa7ec8 --- /dev/null +++ b/core-java-modules/core-java-function/src/test/java/com/baeldung/lazylambda/LazyLambdaThreadSafeSupplierUnitTest.java @@ -0,0 +1,52 @@ +package com.baeldung.lazylambda; + +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.stubbing.Answer; + +public class LazyLambdaThreadSafeSupplierUnitTest { + + @Test + public void whenCalledMultipleTimes_thenShouldBeCalledOnlyOnce() { + @SuppressWarnings("unchecked") Supplier mockedExpensiveFunction = Mockito.mock(Supplier.class); + Mockito.when(mockedExpensiveFunction.get()) + .thenReturn("expensive call"); + LazyLambdaThreadSafeSupplier testee = new LazyLambdaThreadSafeSupplier<>(mockedExpensiveFunction); + Mockito.verify(mockedExpensiveFunction, Mockito.never()) + .get(); + testee.getData(); + testee.getData(); + Mockito.verify(mockedExpensiveFunction, Mockito.times(1)) + .get(); + } + + @Test + public void whenCalledMultipleTimesConcurrently_thenShouldBeCalledOnlyOnce() throws InterruptedException { + @SuppressWarnings("unchecked") Supplier mockedExpensiveFunction = Mockito.mock(Supplier.class); + Mockito.when(mockedExpensiveFunction.get()) + .thenAnswer((Answer) invocation -> { + Thread.sleep(1000L); + return "Late response!"; + }); + LazyLambdaThreadSafeSupplier testee = new LazyLambdaThreadSafeSupplier<>(mockedExpensiveFunction); + Mockito.verify(mockedExpensiveFunction, Mockito.never()) + .get(); + + ExecutorService executorService = Executors.newFixedThreadPool(4); + executorService.invokeAll(List.of(testee::getData, testee::getData)); + executorService.shutdown(); + if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) { + executorService.shutdownNow(); + } + + Mockito.verify(mockedExpensiveFunction, Mockito.times(1)) + .get(); + } + +}