Ich würde Dir Empfehlen, hier die Problematiken deutlich besser aufzuteilen. Du hast hier Themen massiv vermischt, die nicht zu vermischen sind alleine schon, weil diese nicht zu vermischen sind (Weil auf komplett unterschiedlichen Ebenen!). Nehmen wir das Beispiel der Webanwendung:
Du hast da erst einmal die Hi Level Ebene:
- Der Client sendet einen Request
- Der Server macht daraufhin etwas (ohne Details zu betrachten) und schickt dann die Antwort - Request erledigt.
Da gibt es bei dieser Sicht de genannte Problematik einfach nicht, dass der Server da irgendwas macht um dann zu warten ob da evtl. noch weitere Requests kommen. Klar, mit einer Stateful Connection / Session kann man sich da irgendwas bauen, aber das führt dann tatsächlich dazu, dass man dann in diverse Probleme hinein rennt wie z.B. eine direkte Limitierung der Anzahl der möglichen parallelen Clients und so.
Daher bedeutet das doch unter dem Strich, dass Du eine Art Paging hast. Es kommt der Request. Ja, das Ergebnis könnte von mir aus von 0 ... MAX_LONG gehen, aber pro Request kommen genau x Elemente (also z.B. 100 oder 1000). Wenn also der erste Request kommt, dann baust Du den "Generator" von 0...999 ... beim folgenden Request kommt dann 1000 ... 1999, ... Aber natürlich bist Du fertig, wenn Du das Ergebnis da hast.
Das ist ja auch, was Du jetzt schon hast: Du generierst den Generator (allerdings mit Ziel MAX_LONG) um dann nach 999 Elementen zu stoppen.
Klar, es kann jetzt Folgeprobleme geben. Die Erzeugung des Generators kann extrem teuer sein und der Client mag sehr viele Requests abschicken, die bedient werden müssten. Dann kann es Sinn machen, nicht für jeden Request einen neuen Generator zu erzeugen. Das ist aber dann doch keine wirkliche Thematik vom Generator. Der Generator generiert nur. Und das innerhalb gewisser Grenzen. Hier kommen dann einfach die für diese Probleme typischen Lösungen ins Spiel - die alle auf unterschiedlichen Ebenen ansetzen und unabhängig voneinander sind:
a) Caching Möglichkeiten. Dann hast Du halt ggf. bei einem Request doch einen etwas universelleren Generator gebaut. Dieser landet in einem Cache. Anfragen gehen also immer an den Cache um einen Generator zu bekommen. Und der Cache kann die Verwaltung aktiv übernehmen. Ein typisches Beispiel hier wäre z.B. das Connection Pooling.
b) Evtl. hast Du tatsächlich eine Art Session. Welcher Art das ist, ist offen. Wichtig ist dann, dass jeder CLient einen aktiven Gegenpart hat. Und dieser Gegenpart wird verwaltet. Bei der typischen Session wäre das dann tatsächlich der mögliche Timeout, der die Session dann nach gewisser Zeit beendet. Hier hat man aber meist einfach nur, dass Daten vorgehalten werden. Alleine schon, um skalierbar zu bleiben ... Aber das wäre ein (altes) Thema, zu dem man sehr viel findet inkl Dingen wie eine zentrale Verwaltung von Sessions. Eine aktuelle Variante könnte hier sein, dass man sowas über WebSocket anspricht. Dein Client baut also eine Verbindung auf und die kann dann etwas machen. Die Verbindung wird dann irgendwann geschlossen und das umfasst auch den Fall: Webanwendung wird geschlossen.
Oft hat man sowas aber auch komplett getrennt: Irgendwas wird generiert, weil es generiert werden muss. Das läuft dann unabhängig von Clients und so. Einfaches Beispiel: Jahres- oder Monatsabschluss. Das läuft dann im Hintergrund und es wird irgendwas an Daten generiert und gespeichert. Das mag zwar von einem Client angetriggert worden sein, aber das ist dann ein eigenständiger Ablauf. Clients können dann die Ergebnisse abrufen und betrachten.
Was für Ansätze möglich und denkbar sind, hängt von den genauen Anforderungen ab und was da genau gemacht wird. Aber das hat auf den Generator relativ wenig Einfluss. Der Generator mag da dann ggf. unterschiedliche Features benötigen, aber dies sind dann immer Low Level Features - halt auf Ebene des Generators.
Zwar wird die Referenz aus dem Consumer-Thread irgendwann vernichtet, aber der (virtuelle oder echte) Thread hat eine Referenz auf die Queue und ist selbst irgendwo noch referenziert, solange die run-Methode nicht beendet wurde.
Auf diesen konkreten Fall möchte ich noch eingehen: Das ist doch erst einmal kein wirkliches Problem aber es erfordert natürlich ein sauberes Programmieren. In Java wäre es das try-with-resources und AutoClosable. In C# wäre es IDisposable. Dein Consumer Thread ist verantwortlich für den Generator. Und wenn der Consumer Thread fertig ist, dann wird halt der Generator geschlossen. Und der Generator ist natürlich so zu schreiben, dass hier ein Schließen möglich ist. Also wenn Du hier mit eigenen (virtuellen) Threads arbeitest, dann hast diese zu wecken und der Thread muss sich dann auch beenden. (So Du hier per einzelnen Threads vorgehen willst. Denkbar sind auch andere Ansätze aber die kommen halt vor allem aus der Zeit vor virtuellen Threads.)
Also ja: Das sind durchaus typische Dinge und man muss hier tatsächlich sauber entwickeln. Und wenn man dann Java Mittel anschaut, dann kann der Generator sowas wie ein Stream sein - und wenn wir uns Stream<T> ansehen: dieser implementiert AutoClosable...