diff --git a/java/find-eventual-safe-states.java b/java/find-eventual-safe-states.java
new file mode 100644
index 0000000..a611d62
--- /dev/null
+++ b/java/find-eventual-safe-states.java
@@ -0,0 +1,60 @@
+class Solution {
+  private static class DFS {
+    private int[][] graph;
+    private boolean[] visited;
+    private boolean[] open;
+
+    public DFS(int[][] graph) {
+      this.graph = graph;
+      visited = new boolean[graph.length];
+      open = new boolean[graph.length];
+    }
+
+    public boolean hasInCycle(int u) {
+      return open[u];
+    }
+
+    public boolean run(int u) {
+      if (open[u]) {
+        // found loop
+        return true;
+      }
+
+      if (visited[u]) {
+        // searched vertex
+        return false;
+      }
+
+      visited[u] = true;
+
+      open[u] = true;
+      for (var v : graph[u]) {
+        if (run(v)) {
+          // loops stay open
+          return true;
+        }
+      }
+      open[u] = false;
+
+      return false;
+    }
+  }
+
+  public List<Integer> eventualSafeNodes(int[][] graph) {
+    int n = graph.length;
+
+    var runner = new DFS(graph);
+    for (var i = 0; i < n; ++i) {
+      runner.run(i);
+    }
+
+    var safe = new ArrayList<Integer>();
+    for (var i = 0; i < n; ++i) {
+      if (!runner.hasInCycle(i)) {
+        safe.add(i);
+      }
+    }
+
+    return safe;
+  }
+}