diff --git a/core-java-modules/core-java-perf-2/README.md b/core-java-modules/core-java-perf-2/README.md
index 0d8c2d798e..8911c687a0 100644
--- a/core-java-modules/core-java-perf-2/README.md
+++ b/core-java-modules/core-java-perf-2/README.md
@@ -5,3 +5,4 @@ This module contains articles about performance of Java applications
### Relevant Articles:
- [Possible Root Causes for High CPU Usage in Java](https://www.baeldung.com/java-high-cpu-usage-causes)
- [External Debugging With JMXTerm](https://www.baeldung.com/java-jmxterm-external-debugging)
+- [Create and Detect Memory Leaks in Java](https://www.baeldung.com/java-create-detect-memory-leaks)
diff --git a/core-java-modules/core-java-perf-2/pom.xml b/core-java-modules/core-java-perf-2/pom.xml
index a9408ca385..a44f6aa8c1 100644
--- a/core-java-modules/core-java-perf-2/pom.xml
+++ b/core-java-modules/core-java-perf-2/pom.xml
@@ -13,4 +13,11 @@
0.0.1-SNAPSHOT
+
+
+ net.datafaker
+ datafaker
+ 1.6.0
+
+
\ No newline at end of file
diff --git a/core-java-modules/core-java-perf-2/src/main/java/com/baeldung/lapsedlistener/LapsedListenerRunner.java b/core-java-modules/core-java-perf-2/src/main/java/com/baeldung/lapsedlistener/LapsedListenerRunner.java
new file mode 100644
index 0000000000..50da98e6c4
--- /dev/null
+++ b/core-java-modules/core-java-perf-2/src/main/java/com/baeldung/lapsedlistener/LapsedListenerRunner.java
@@ -0,0 +1,35 @@
+package com.baeldung.lapsedlistener;
+
+import static com.baeldung.lapsedlistener.UserGenerator.generateUser;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class LapsedListenerRunner {
+
+ private static final Logger logger = LoggerFactory.getLogger(LapsedListenerRunner.class);
+ private static final MovieQuoteService movieQuoteService = new MovieQuoteService();
+
+ static {
+ movieQuoteService.start();
+ }
+
+public static void main(String[] args) {
+ while (true) {
+ User user = generateUser();
+ logger.debug("{} logged in", user.getName());
+ user.subscribe(movieQuoteService);
+ userUsingService();
+ logger.debug("{} logged out", user.getName());
+ }
+}
+
+ private static void userUsingService() {
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/core-java-modules/core-java-perf-2/src/main/java/com/baeldung/lapsedlistener/MovieQuoteService.java b/core-java-modules/core-java-perf-2/src/main/java/com/baeldung/lapsedlistener/MovieQuoteService.java
new file mode 100644
index 0000000000..95240bbbd5
--- /dev/null
+++ b/core-java-modules/core-java-perf-2/src/main/java/com/baeldung/lapsedlistener/MovieQuoteService.java
@@ -0,0 +1,47 @@
+package com.baeldung.lapsedlistener;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import net.datafaker.Faker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MovieQuoteService implements Subject {
+
+ private static final Logger logger = LoggerFactory.getLogger(MovieQuoteService.class);
+ private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
+ private final List observers = new ArrayList<>();
+ private final Faker faker = new Faker();
+
+ @Override
+ public void attach(Observer observer) {
+ logger.debug("Current number of subscribed users: {}", observers.size());
+ observers.add(observer);
+ }
+
+ @Override
+ public void detach(Observer observer) {
+ observers.remove(observer);
+ }
+
+ @Override
+ public void notifyObservers() {
+ String quote = faker.movie().quote();
+ logger.debug("New quote: {}", quote);
+ for (Observer observer : observers) {
+ logger.debug("Notifying user: {}", observer);
+ observer.update(quote);
+ }
+ }
+
+ public void start() {
+ scheduler.scheduleAtFixedRate(this::notifyObservers, 0, 1, TimeUnit.SECONDS);
+ }
+
+ public int numberOfSubscribers() {
+ return observers.size();
+ }
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-perf-2/src/main/java/com/baeldung/lapsedlistener/Observer.java b/core-java-modules/core-java-perf-2/src/main/java/com/baeldung/lapsedlistener/Observer.java
new file mode 100644
index 0000000000..2c4ccf9e06
--- /dev/null
+++ b/core-java-modules/core-java-perf-2/src/main/java/com/baeldung/lapsedlistener/Observer.java
@@ -0,0 +1,9 @@
+package com.baeldung.lapsedlistener;
+
+public interface Observer {
+ void update(String latestNews);
+
+ void subscribe(Subject subject);
+
+ void unsubscribe(Subject subject);
+}
diff --git a/core-java-modules/core-java-perf-2/src/main/java/com/baeldung/lapsedlistener/Subject.java b/core-java-modules/core-java-perf-2/src/main/java/com/baeldung/lapsedlistener/Subject.java
new file mode 100644
index 0000000000..141a7662a7
--- /dev/null
+++ b/core-java-modules/core-java-perf-2/src/main/java/com/baeldung/lapsedlistener/Subject.java
@@ -0,0 +1,9 @@
+package com.baeldung.lapsedlistener;
+
+public interface Subject {
+ void attach(Observer observer);
+
+ void detach(Observer observer);
+
+ void notifyObservers();
+}
diff --git a/core-java-modules/core-java-perf-2/src/main/java/com/baeldung/lapsedlistener/User.java b/core-java-modules/core-java-perf-2/src/main/java/com/baeldung/lapsedlistener/User.java
new file mode 100644
index 0000000000..0ed0a32a01
--- /dev/null
+++ b/core-java-modules/core-java-perf-2/src/main/java/com/baeldung/lapsedlistener/User.java
@@ -0,0 +1,70 @@
+package com.baeldung.lapsedlistener;
+
+public class User implements Observer {
+
+ private final String name;
+ private final String email;
+ private final String phone;
+ private final String street;
+ private final String city;
+ private final String state;
+ private final String zipCode;
+
+ public User(String name, String email, String phone, String street, String city, String state, String zipCode) {
+ this.name = name;
+ this.email = email;
+ this.phone = phone;
+ this.street = street;
+ this.city = city;
+ this.state = state;
+ this.zipCode = zipCode;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public String getPhone() {
+ return phone;
+ }
+
+ public String getStreet() {
+ return street;
+ }
+
+ public String getCity() {
+ return city;
+ }
+
+ public String getState() {
+ return state;
+ }
+
+ public String getZipCode() {
+ return zipCode;
+ }
+
+ @Override
+ public void update(final String quote) {
+ // user got updated with a new quote
+ }
+
+ @Override
+ public void subscribe(final Subject subject) {
+ subject.attach(this);
+ }
+
+ @Override
+ public void unsubscribe(final Subject subject) {
+ subject.detach(this);
+ }
+
+ @Override
+ public String toString() {
+ return "User [name=" + name + "]";
+ }
+}
diff --git a/core-java-modules/core-java-perf-2/src/main/java/com/baeldung/lapsedlistener/UserGenerator.java b/core-java-modules/core-java-perf-2/src/main/java/com/baeldung/lapsedlistener/UserGenerator.java
new file mode 100644
index 0000000000..acdb7d7af7
--- /dev/null
+++ b/core-java-modules/core-java-perf-2/src/main/java/com/baeldung/lapsedlistener/UserGenerator.java
@@ -0,0 +1,23 @@
+package com.baeldung.lapsedlistener;
+
+import net.datafaker.Faker;
+
+public class UserGenerator {
+
+ private UserGenerator() {
+ }
+
+ private static final Faker faker = new Faker();
+
+ public static User generateUser() {
+ String name = faker.name().fullName();
+ String email = faker.internet().emailAddress();
+ String phone = faker.phoneNumber().cellPhone();
+ String street = faker.address().streetAddress();
+ String city = faker.address().city();
+ String state = faker.address().state();
+ String zipCode = faker.address().zipCode();
+ return new User(name, email, phone, street, city, state, zipCode);
+ }
+
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-perf-2/src/test/java/com/baeldung/lapsedlistener/MovieQuoteServiceTest.java b/core-java-modules/core-java-perf-2/src/test/java/com/baeldung/lapsedlistener/MovieQuoteServiceTest.java
new file mode 100644
index 0000000000..90c96cba1b
--- /dev/null
+++ b/core-java-modules/core-java-perf-2/src/test/java/com/baeldung/lapsedlistener/MovieQuoteServiceTest.java
@@ -0,0 +1,24 @@
+package com.baeldung.lapsedlistener;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+class MovieQuoteServiceTest {
+
+ @Test
+ void whenSubscribeToService_thenServiceHasOneSubscriber() {
+ MovieQuoteService service = new MovieQuoteService();
+ service.attach(UserGenerator.generateUser());
+ assertEquals(1, service.numberOfSubscribers());
+ }
+
+ @Test
+ void whenUnsubscribeFromService_thenServiceHasNoSubscribers() {
+ MovieQuoteService service = new MovieQuoteService();
+ User user = UserGenerator.generateUser();
+ service.attach(user);
+ service.detach(user);
+ assertEquals(0, service.numberOfSubscribers());
+ }
+}
\ No newline at end of file