This is an automated email from the ASF dual-hosted git repository.
paulk-asert pushed a commit to branch asf-site
in repository https://gitbox.apache.org/repos/asf/groovy-website.git
The following commit(s) were added to refs/heads/asf-site by this push:
new aa654a4 Three blog posts updated to a more recent Pekko version and
recommending Groovy 6 concurrency features ahead of GPars once you migrate to
Groovy 6.
aa654a4 is described below
commit aa654a480b7d70aaae30801f9bc762c16c5bd3cf
Author: Paul King <[email protected]>
AuthorDate: Sun Jul 12 13:56:36 2026 +1000
Three blog posts updated to a more recent Pekko version and recommending
Groovy 6 concurrency features ahead of GPars once you migrate to Groovy 6.
---
.../src/site/blog/gpars-meets-virtual-threads.adoc | 15 +-
site/src/site/blog/groovy-async-await.adoc | 36 ++--
site/src/site/blog/groovy-pekko-gpars.adoc | 201 +++++++++++----------
3 files changed, 134 insertions(+), 118 deletions(-)
diff --git a/site/src/site/blog/gpars-meets-virtual-threads.adoc
b/site/src/site/blog/gpars-meets-virtual-threads.adoc
index eda53e2..0434778 100644
--- a/site/src/site/blog/gpars-meets-virtual-threads.adoc
+++ b/site/src/site/blog/gpars-meets-virtual-threads.adoc
@@ -1,7 +1,7 @@
= GPars meets Virtual Threads
Paul King
:revdate: 2022-06-15T11:28:56+00:00
-:updated: 2023-04-14T18:23:00+00:00
+:updated: 2026-07-12T00:00:00+00:00
:keywords: concurrency, groovy, virtual threads, actors, dataflow, agents
:description: This post looks at using GPars with virtual threads.
@@ -23,6 +23,19 @@ If you want to try these out, use the latest JDK21ea version
which has virtual
thread support as standard. Or use a recent JDK19-20 version
and enable _preview_ features with your Groovy tooling.
+[NOTE]
+====
+*Groovy 6 update:* Since this post was written, Groovy 6 added a native
+concurrency and parallel-processing toolkit in the `groovy.concurrent` package
+(GEP-18) — parallel collections, agents, actors, dataflow variables, and
+Go-style channels — all virtual-thread-first and composable with
+https://groovy.apache.org/blog/groovy-async-await[`async`/`await`]. If you are
on
+Groovy 6 or later, prefer those built-in features; a good starting point is the
+https://groovy.apache.org/blog/groovy-async-await[Async/await for Groovy] post.
+This post remains a useful reference if you are on an earlier Groovy version or
+are maintaining an existing GPars codebase.
+====
+
== Parallel Collections
First a refresher, we'll first look at using the GPars parallel collections
feature
diff --git a/site/src/site/blog/groovy-async-await.adoc
b/site/src/site/blog/groovy-async-await.adoc
index c74d147..48c71ea 100644
--- a/site/src/site/blog/groovy-async-await.adoc
+++ b/site/src/site/blog/groovy-async-await.adoc
@@ -1,7 +1,7 @@
= Async/await for Groovy™
Paul King <paulk-asert|PMC_Member>
:revdate: 2026-03-27T16:30:00+00:00
-:updated: 2026-04-06T23:35:00+00:00
+:updated: 2026-07-12T00:00:00+00:00
:keywords: async, await, concurrency, virtual-threads
:description: Groovy's async/await: write concurrent code that reads like
synchronous code, with virtual thread support, generators, channels, &
structured concurrency.
@@ -453,24 +453,25 @@ or thread-safe types from `java.util.concurrent`.
| Transparent `await` / `for await` with Reactor or RxJava types.
|===
-== How it relates to GPars and virtual threads
+== How it relates to groovy.concurrent and GPars
-Readers of the
-https://groovy.apache.org/blog/gpars-meets-virtual-threads[GPars meets virtual
threads]
-blog post will recall that GPars provides parallel collections,
-actors, agents, and dataflow concurrency.
-
-Async/await complements GPars rather than replacing it. GPars
-excels at data-parallel operations and actor-based designs.
-Async/await targets sequential-looking code that is actually
-asynchronous, with language-level support for streams, cleanup,
-structured concurrency, and framework bridging.
+Async/await is one part of Groovy 6's broader concurrency toolkit. The same
+`groovy.concurrent` package also provides parallel collections, actors, agents,
+dataflow variables, and Go-style channels (GEP-18), and they all compose with
+the `await` and `Awaitable` combinators shown above — so you build solutions
+from one consistent toolkit rather than learning separate APIs.
-GPars' `callAsync()` and `asyncFun()` return futures that work
-naturally with `await` and the `Awaitable` combinators, so you
-can mix and match both styles in the same codebase.
+Before Groovy 6, these features weren't built in and the
+http://www.gpars.org/[GPars] library was the popular choice for parallel
+collections, actors, agents, and dataflow. The
+https://groovy.apache.org/blog/gpars-meets-virtual-threads[GPars meets virtual
threads]
+post shows those GPars features. If you are on Groovy 6 or later, prefer the
+built-in `groovy.concurrent` equivalents; GPars remains useful for earlier
+Groovy versions and existing GPars codebases. GPars' `callAsync()` and
+`asyncFun()` return futures that work naturally with `await` and the
`Awaitable`
+combinators, so you can mix and match both styles during a migration.
-Both approaches benefit from virtual threads on JDK 21+.
+All of these approaches benefit from virtual threads on JDK 21+.
== Conclusion
@@ -496,5 +497,6 @@ concurrent — not the method signature.
.Update history
****
*27/Mar/2026*: Initial version. +
-*06/Apr/2026*: Revised version after feedback including numerous
simplifications.
+*06/Apr/2026*: Revised version after feedback including numerous
simplifications. +
+*12/Jul/2026*: Reframe the GPars section around the built-in
`groovy.concurrent` package.
****
diff --git a/site/src/site/blog/groovy-pekko-gpars.adoc
b/site/src/site/blog/groovy-pekko-gpars.adoc
index 85b4583..f5cbd2a 100644
--- a/site/src/site/blog/groovy-pekko-gpars.adoc
+++ b/site/src/site/blog/groovy-pekko-gpars.adoc
@@ -1,9 +1,9 @@
-= Using Apache Pekko actors and GPars actors with Groovy™
+= Using Apache Pekko actors and Groovy™ 6 actors
Paul King
:revdate: 2023-07-17T23:24:56+00:00
-:updated: 2024-09-28T04:59:00+00:00
-:keywords: concurrency, groovy, actors, pekko, gpars
-:description: This post looks at using Apache Pekko Actors and GPars Actors
with Groovy.
+:updated: 2026-07-12T00:00:00+00:00
+:keywords: concurrency, groovy, actors, pekko, groovy.concurrent, gpars
+:description: This post looks at using Apache Pekko Actors and Groovy 6's
built-in groovy.concurrent Actors.
image:https://pekko.apache.org/assets/images/pekko_logo.png[pekko
logo,100,float="right"]
https://pekko.apache.org/[Apache Pekko] is an Apache-licensed fork of the Akka
project (based on Akka version 2.6.x) and provides a
@@ -13,14 +13,23 @@ as well as additional libraries for persistence, streams,
HTTP, and more.
It provides Scala and Java APIs/DSLs for writing your applications. We'll be
using the latter.
We'll look at just one example of using Pekko actors.
-image:img/gpars_logo.png[gpars,180,float="right"]
-By way of comparison, we'll also be looking at http://www.gpars.org/[GPars],
-a concurrency library for Java and Groovy with support for actors, agents,
-concurrent & parallel map/reduce, fork/join, asynchronous closures, dataflow,
and more.
-A https://groovy.apache.org/blog/gpars-meets-virtual-threads[previous blog
post]
-looks at additional features of GPars and how to use it with virtual threads.
+By way of comparison, we'll also look at the actor support built into
+*Groovy 6* in the `groovy.concurrent` package (GEP-18). Alongside actors,
+that package provides agents, dataflow variables, Go-style channels, and
+parallel collections — all virtual-thread-first and composable with Groovy's
+https://groovy.apache.org/blog/groovy-async-await[`async`/`await`] support.
Here, we'll just look at the comparable actor features for our Pekko example.
+[NOTE]
+====
+Before Groovy 6, these actor features weren't built in and the
+http://www.gpars.org/[GPars] library was the popular choice. If you are on an
+earlier Groovy version or maintaining an existing GPars codebase, the
+https://groovy.apache.org/blog/gpars-meets-virtual-threads[GPars meets virtual
+threads] post shows the equivalent GPars examples. The companion repository
+includes a legacy GPars version of this example too.
+====
+
== The example
A common first example involving actors involves creating two actors where one
actor
@@ -45,8 +54,8 @@ to tell it the maximum number of times to echo the message
before stopping.
== A Pekko implementation in Groovy
-This example uses Groovy 4.0.23 and Pekko 1.1.1.
-It was tested with JDK 11, 17, 21 and 23.
+This example uses Groovy 6.0.0-alpha-2 and Pekko 1.6.0.
+It was tested with JDK 21 and 25.
The Pekko documentation gives Java and Scala implementations.
You should notice that the Groovy implementation is similar to the Java one
@@ -195,159 +204,151 @@ The log output from running the script will look
similar to this:
[hello-pekko.actor.default-dispatcher-6] INFO
org.apache.pekko.actor.CoordinatedShutdown - Running CoordinatedShutdown with
reason [ActorSystemTerminateReason]
----
-== A GPars implementation in Groovy
+== A Groovy 6 implementation using groovy.concurrent
-This example uses Groovy 4.0.23 and GPars 1.2.1.
-It was tested with JDK 8, 11, 17, 21 and 23.
+Groovy 6 ships a native concurrency toolkit in the `groovy.concurrent`
+package (GEP-18). Among other features, it provides lightweight,
+virtual-thread-friendly actors that compose with Groovy's
+https://groovy.apache.org/blog/groovy-async-await[`async`/`await`] support.
-We'll follow the same conventions for strongly typed messages in our GPars
example.
-Here are our three message containers:
+We'll follow the same conventions for strongly typed messages.
+Because our actors reference one another, we parameterize each `Actor` with the
+message type it accepts. Here are our three message records:
[source,groovy]
----
-record Greet(String whom, Actor replyTo) { }
+import groovy.concurrent.Actor
+
+record Greet(String whom, Actor<Greeted> replyTo) { }
-record Greeted(String whom, Actor from) {}
+record Greeted(String whom, Actor<Greet> from) { }
record SayHello(String name) { }
----
-Now we'll define our `helloWorld` actor:
+Now we'll define our `greeter` actor. A _reactor_ actor is stateless: its
+closure handles each incoming message. Here we don't need to reply directly,
+so we send a `Greeted` message on to the reply address ourselves:
[source,groovy]
----
-greeter = actor {
- loop {
- react { Greet command ->
- println "Hello $command.whom!"
- command.replyTo << new Greeted(command.whom, greeter)
- }
- }
+Actor<Greet> greeter
+greeter = Actor.reactor { Greet command ->
+ println "Hello $command.whom!"
+ command.replyTo.send(new Greeted(command.whom, greeter))
}
----
-Here, we are using GPars Groovy continuation-style DSL for defining actors.
-The `loop` indicates that the actor will loop continually.
-When we receive the `Greet` message, we log the details to stdout and
-send the acknowledgement.
-
-If we don't want to use the DSL syntax, we can use the related classes
directly.
-Here we'll define a `HelloWorldBot` using this slightly more verbose style.
-It shows adding the state variables we need for tracking the invocation count:
+Our bot needs to remember how many greetings it has seen, so we use a
+_stateful_ actor. We seed it with an initial state of `0`; the handler
+receives the current state and the message, and returns the next state:
[source,groovy]
----
-class HelloWorldBot extends DefaultActor {
- int max
- private int greetingCounter = 0
-
- @Override
- protected void act() {
- loop {
- react { Greeted message ->
- greetingCounter++
- println "Greeting $greetingCounter for $message.whom"
- if (greetingCounter < max) message.from << new
Greet(message.whom, this)
- else terminate()
- }
- }
+def newBot = { int max ->
+ Actor<Greeted> bot
+ bot = Actor.stateful(0) { int count, Greeted message ->
+ int next = count + 1
+ println "Greeting $next for $message.whom"
+ if (next < max) message.from.send(new Greet(message.whom, bot))
+ else bot.stop()
+ next
}
+ bot
}
----
-Our main actor is very simple. It is waiting for `SayHello` messages, and when
it receives one,
-it sends the payload to the helloWorld greeter telling it to reply to a newly
created _bot_.
+Our main actor is very simple. It waits for `SayHello` messages, and when it
receives one,
+it sends the payload to the greeter, telling it to reply to a newly created
_bot_.
[source,groovy]
----
-var main = actor {
- loop {
- react { SayHello command ->
- greeter << new Greet(command.name, new HelloWorldBot(max:
3).start())
- }
- }
+def main = Actor.reactor { SayHello command ->
+ greeter.send(new Greet(command.name, newBot(3)))
}
----
-Finally, we start the system going by sending some initial messages:
+Finally, we start the system going by sending some initial messages,
+give the actors a moment to finish, then stop them:
[source,groovy]
----
-main << new SayHello('World')
-main << new SayHello('GPars')
+main.send(new SayHello('World'))
+main.send(new SayHello('Groovy 6'))
+
+sleep 2000
+main.stop()
+greeter.stop()
----
-The output looks like this:
+The output looks like this (the exact interleaving varies between runs):
----
Hello World!
-Hello GPars!
+Hello Groovy 6!
+Greeting 1 for Groovy 6
Greeting 1 for World
-Greeting 1 for GPars
+Hello Groovy 6!
Hello World!
-Hello GPars!
+Greeting 2 for Groovy 6
+Hello Groovy 6!
Greeting 2 for World
+Greeting 3 for Groovy 6
Hello World!
-Greeting 2 for GPars
-Hello GPars!
Greeting 3 for World
-Greeting 3 for GPars
----
== Discussion
-The GPars implementation is less verbose compared to the Pekko implementation
but Pekko
-is known for providing additional type safety for actor messages and that is
partly what we are seeing.
+The `groovy.concurrent` implementation is less verbose than the Pekko
+implementation. Pekko is known for providing additional type safety for
+actor messages, and that is partly what we saw in the Pekko version, where
+each actor is a `Behavior<T>` for a specific message type. Pekko also brings a
+much larger feature set — clustering, persistence, streams, and distribution —
+so it remains the right choice when you need those capabilities.
-GPars supports a mixture of styles, some offering less verbosity at the
expense of capturing some
-errors at runtime rather than compile-time. Such code can be useful when
wanting very succinct code
-using Groovy's dynamic nature. When using Groovy's static nature or Java, you
might consider using
-select parts of the GPars API.
+The core Groovy actors are typed too: an `Actor<Greet>` only accepts `Greet`
+messages, so message sends are checked when you compile statically (for example
+with `@CompileStatic` or `@TypeChecked`) while remaining convenient for dynamic
+Groovy. You choose between a stateless `Actor.reactor` and a stateful
+`Actor.stateful` depending on whether the actor needs to remember anything
+between messages.
-For example, we can provide an alternative definition for `HelloWorldBot` like
this:
+Because core actors integrate with `async`/`await`, request/reply is
+straightforward. Rather than wiring up an explicit reply address, a reactor can
+simply _return_ a value and the caller can `await` the reply from `sendAndGet`:
[source,groovy]
----
-class HelloWorldBot extends StaticDispatchActor<Greeted> {
- int max
- private int greetingCounter = 0
-
- @Override
- void onMessage(Greeted message) {
- greetingCounter++
- println "Greeting $greetingCounter for $message.whom"
- if (greetingCounter < max) message.from << new Greet(message.whom,
this)
- else terminate()
- }
-}
+def doubler = Actor.reactor { it * 2 }
+assert await(doubler.sendAndGet(21)) == 42
+doubler.stop()
----
-The `StaticDispatchActor` dispatches the message solely based on the
compile-time information.
-This can be more efficient when dispatching based on message run-time type is
not necessary.
-
-We could also provide an alternative definition for `Greet` as follows:
-
-[source,groovy]
-----
-record Greet(String whom, StaticDispatchActor<Greeted> replyTo) { }
-----
-
-With changes like these in place we can code a solution with additional
message type safety
-when using Groovy's static nature.
+The same building blocks — agents, dataflow variables, channels, and parallel
+collections — all live in `groovy.concurrent` and compose with the same
+async/await combinators, so you build up solutions from a single, consistent
+toolkit rather than learning separate APIs. See the
+https://groovy.apache.org/blog/groovy-async-await[Async/await for Groovy] post
+for the bigger picture.
== Conclusion
-We have had a quick glimpse at using actors with Apache Pekko and GPars.
+We have had a quick glimpse at using actors with Apache Pekko and with Groovy
6's
+built-in `groovy.concurrent` package (with GPars noted as the pre-Groovy-6
option).
The sample code can be found here:
https://github.com/paulk-asert/groovy-pekko-gpars
+It contains `pekko`, `groovy6`, and legacy `gpars` versions of the example.
+
.Update history
****
*17/Jul/2023*: Initial version. +
*18/Jul/2023*: Add discussion about type-safe messages. +
*26/Jul/2023*: Update to Pekko 1.0.1. +
*25/Jun/2024*: Update to Pekko 1.0.3. +
-*28/Sep/2024*: Update to Pekko 1.1.1.
+*28/Sep/2024*: Update to Pekko 1.1.1. +
+*12/Jul/2026*: Update to Pekko 1.6.0 and Groovy 6; replace the GPars example
with the built-in `groovy.concurrent` actors (GPars retained as a legacy
reference).
****