You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2023/02/11 10:30:06 UTC
[groovy-website] 01/02: convert another blog
This is an automated email from the ASF dual-hosted git repository.
paulk pushed a commit to branch asf-site
in repository https://gitbox.apache.org/repos/asf/groovy-website.git
commit f2f196801dcfdad9488b1daf883cb4a98dbcf038
Author: paulk-asert <pa...@asert.com.au>
AuthorDate: Sat Feb 11 20:29:27 2023 +1000
convert another blog
---
.../src/site/blog/gpars-meets-virtual-threads.adoc | 180 +++++++++++++++++++++
site/src/site/blog/gpars-meets-virtual-threads.md | 44 -----
2 files changed, 180 insertions(+), 44 deletions(-)
diff --git a/site/src/site/blog/gpars-meets-virtual-threads.adoc b/site/src/site/blog/gpars-meets-virtual-threads.adoc
new file mode 100644
index 0000000..da35417
--- /dev/null
+++ b/site/src/site/blog/gpars-meets-virtual-threads.adoc
@@ -0,0 +1,180 @@
+= GPars meets Virtual Threads
+:revdate: 2022-06-15T11:28:56+00:00
+:keywords: concurrency, groovy, virtual threads, actors, dataflow, agents
+:description: Thi post looks at using GPars with virtual threads.
+
+image:img/gpars_logo.png[gpars,150,float="right"]
+An exciting preview feature coming in JDK19 is Virtual Threads
+(https://openjdk.java.net/jeps/425[JEP 425]). In my experiments
+so far, virtual threads work well with my favourite Groovy parallel
+and concurrency library http://gpars.org/[GPars]. GPars has been
+around a while (since Java 5 and Groovy 1.8 days) but still has
+many useful features. Let's have a look at a few examples.
+
+If you want to try these out, make sure you have a recent JDK19
+(currently EA) and enable _preview_ features with your Groovy
+tooling.
+
+== Parallel Collections
+
+First a refresher, to use the GPars parallel collections feature
+with normal threads, use the GParsPool.withPool method as follows:
+
+[source,groovy]
+----
+withPool {
+ assert [1, 2, 3].collectParallel{ it ** 2 } == [1, 4, 9]
+}
+----
+
+For any Java readers, don't get confused with the `collectParallel`
+method name. Groovy's `collect` method (naming inspired by
+Smalltalk) is the equivalent of Java's `map` method. So, the
+equivalent Groovy code using the Java streams API would be
+something like:
+
+[source,groovy]
+----
+assert [1, 2, 3].parallelStream().map(n -> n ** 2).collect(Collectors.toList()) == [1, 4, 9]
+----
+
+Now, let's bring virtual threads into the picture. Luckily,
+GPars parallel collection facilities provide a hook for using
+an _existing_ custom executor service. This makes using virtual
+threads for such code easy:
+
+[source,groovy]
+----
+withExistingPool(Executors.newVirtualThreadPerTaskExecutor()) {
+ assert [1, 2, 3].collectParallel{ it ** 2 } == [1, 4, 9]
+}
+----
+
+Nice! But let's move onto some areas examples which might be
+less familiar to Java developers.
+
+GPars has additional features for providing custom thread pools
+and the remaining examples rely on those features. The current
+version of GPars doesn't have a `DefaultPool` constructor that
+takes a vanilla executor service, so, we'll write our own class:
+
+[source,groovy]
+----
+@AutoImplement
+class VirtualPool implements Pool {
+ private final ExecutorService pool = Executors.newVirtualThreadPerTaskExecutor()
+ int getPoolSize() { pool.poolSize }
+ void execute(Runnable task) { pool.execute(task) }
+ ExecutorService getExecutorService() { pool }
+}
+----
+
+It is essentially a delegate from the GPars `Pool` interface
+to the virtual threads executor service.
+
+We'll use this in the remaining examples.
+
+== Agents
+
+Agents provide a thread-safe non-blocking wrapper around an
+otherwise potentially mutable shared state object. They are
+inspired by agents in Clojure.
+
+In our case we'll use an agent to "protect" a plain `ArrayList`.
+For this simple case, we could have used some synchronized list,
+but in general, agents eliminate the need to find thread-safe
+implementation classes or indeed care at all about the thread
+safety of the underlying wrapped object.
+
+[source,groovy]
+----
+def mutableState = [] // a non-synchronized mutable list
+def agent = new Agent(mutableState)
+
+agent.attachToThreadPool(new VirtualPool()) // omit line for normal threads
+
+agent { it << 'Dave' } // one thread updates list
+agent { it << 'Joe' } // another thread also updating
+assert agent.val.size() == 2
+----
+
+== Actors
+
+Actors allow for a message passing-based concurrency model.
+The actor model ensures that at most one thread processes
+the actor's body at any time. The GPars API and DSLs for actors
+are quite rich supporting many features. We'll look at a simple
+example here.
+
+GPars manages actor thread pools in groups.
+Let's create one backed by virtual threads:
+
+[source,groovy]
+----
+def vgroup = new DefaultPGroup(new VirtualPool())
+----
+
+Now we can write an encrypting and decrypting actor pair as follows:
+
+[source,groovy]
+----
+def decryptor = vgroup.actor {
+ loop {
+ react { String message ->
+ reply message.reverse()
+ }
+ }
+}
+
+def console = vgroup.actor {
+ decryptor << 'lellarap si yvoorG'
+ react {
+ println 'Decrypted message: ' + it
+ }
+}
+
+console.join() // output: Decrypted message: Groovy is parallel
+----
+
+== Dataflow
+
+Dataflow offers an inherently safe and robust declarative
+concurrency model. Dataflows are also managed via thread
+groups, so we'll use `vgroup` which we created earlier.
+
+We have three logical tasks which can run in parallel and perform
+their work. The tasks need to exchange data and they do so using
+_dataflow variables_. Think of dataflow variables as one-shot
+channels safely and reliably transferring data from producers to
+their consumers.
+
+[source,groovy]
+----
+def df = new Dataflows()
+
+vgroup.task {
+ df.z = df.x + df.y
+}
+
+vgroup.task {
+ df.x = 10
+}
+
+vgroup.task {
+ df.y = 5
+}
+
+assert df.z == 15
+----
+
+The dataflow framework works out how to schedule the individual
+tasks and ensures that a task's input variables are ready when
+needed.
+
+== Conclusion
+
+We have had a quick glimpse at using virtual threads with Groovy
+and GPars. It is very early days, so expect much more to emerge
+in this space once virtual threads are released in preview in
+production versions of JDK19 and eventually beyond a preview
+feature.
diff --git a/site/src/site/blog/gpars-meets-virtual-threads.md b/site/src/site/blog/gpars-meets-virtual-threads.md
deleted file mode 100644
index fdc7047..0000000
--- a/site/src/site/blog/gpars-meets-virtual-threads.md
+++ /dev/null
@@ -1,44 +0,0 @@
----
-layout: post
-title: GPars meets Virtual Threads
-date: '2022-06-15T11:28:56+00:00'
-permalink: gpars-meets-virtual-threads
----
-<p><img src="https://blogs.apache.org/groovy/mediaresource/52d821d8-f108-48e6-805d-f8fdd514f9ca" style="width: 20%;" align="right" alt="gpars-rgb.png">An exciting preview feature coming in JDK19 is Virtual Threads (<a href="https://openjdk.java.net/jeps/425" target="_blank">JEP 425</a>). In my experiments so far, virtual threads work well with my favourite Groovy parallel and concurrency library <a href="http://gpars.org/" target="_blank">GPars</a>. GPars has been around a while (since J [...]
-
-<h3>Parallel Collections</h3>
-
-<p>First a refresher, to use the GPars parallel collections feature with normal threads, use the
-<span style="background-color: #FFFFFF; color: rgb(8, 8, 8); font-family: "JetBrains Mono", monospace; font-size: 14px;> GParsPool.<span style=" font-style="italic;"">withPool</span> method as follows:
-</p>
-
-<pre style="color:#080808;font-family:'JetBrains Mono',monospace;font-size:9.6pt;"><span style="font-style:italic;">withPool </span><span style="font-weight:bold;">{<br></span><span style="font-weight:bold;"> </span><span style="color:#0033b3;">assert </span>[<span style="color:#1750eb;">1</span>, <span style="color:#1750eb;">2</span>, <span style="color:#1750eb;">3</span>].collectParallel<span style="font-weight:bold;">{ </span>it ** <span style="color:#1750eb;">2 </span><span style= [...]
-}</pre>
-
-<p>For any Java readers, don't get confused with the <span style="font-family:'JetBrains Mono',monospace;font-size: 14px; color: rgb(8, 8, 8);">collectParallel</span> method name. Groovy's <span style="font-family:'JetBrains Mono',monospace;font-size: 14px; color: rgb(8, 8, 8);">collect</span> method (naming inspired by Smalltalk) is the equivalent of Java's <span style="font-family:'JetBrains Mono',monospace;font-size: 14px; color: rgb(8, 8, 8);">map</span> method. So, the equivalent Gr [...]
-
-</p><pre style="color:#080808;font-family:'JetBrains Mono',monospace;font-size:9.6pt;"><span style="color:#0033b3;">assert </span>[<span style="color:#1750eb;">1</span>, <span style="color:#1750eb;">2</span>, <span style="color:#1750eb;">3</span>].parallelStream().map(n -> n ** <span style="color:#1750eb;">2</span>).collect(<span style="color:#000000;">Collectors</span>.<span style="font-style:italic;">toList</span>()) == [<span style="color:#1750eb;">1</span>, <span style="color:#175 [...]
-
-<p>Now, let's bring virtual threads into the picture. Luckily, GPars parallel collection facilities provide a hook for using an <i>existing </i>custom executor service. This makes using virtual threads for such code easy:</p><pre style="color:#080808;font-family:'JetBrains Mono',monospace;font-size:9.6pt;"><span style="font-style:italic;">withExistingPool</span>(<span style="color:#000000;">Executors</span>.newVirtualThreadPerTaskExecutor()) <span style="font-weight:bold;">{<br></span><s [...]
-<p>Nice! But let's move onto some areas examples which might be less familiar to Java developers.</p><p>GPars has additional features for providing custom thread pools and the remaining examples rely on those features. The current version of GPars doesn't have a <span style="font-family: "JetBrains Mono", monospace; font-size: 9.6pt;">DefaultPool </span>constructor that takes a vanilla executor service, so, we'll write our own class:</p><pre style="color:#080808;font- [...]
-
-<h3>Agents</h3>
-<p>Agents provide a thread-safe non-blocking wrapper around an otherwise potentially mutable shared state object. They are inspired by agents in Clojure.</p><p>In our case we'll use an agent to "protect" a plain <span style="font-family: "JetBrains Mono", monospace; font-size: 9.6pt;">ArrayList</span>. For this simple case, we could have used some synchronized list, but in general, agents eliminate the need to find thread-safe implementation classes or indeed care at all a [...]
-<pre style="color:#080808;font-family:'JetBrains Mono',monospace;font-size:9.6pt;"><span style="color:#0033b3;">def </span><span style="color:#000000;">mutableState </span>= [] <span style="color:#8c8c8c;font-style:italic;">// a non-synchronized mutable list<br></span><span style="color:#0033b3;">def </span><span style="color:#000000;">agent </span>= <span style="color:#0033b3;">new </span><span style="color:#000000;">Agent</span>(<span style="color:#000000;">mutableState</span>)<br> [...]
-
-<h3>Actors</h3>
-<p>Actors allow for a message passing-based concurrency model. The actor model ensures that at most one thread processes the actor's body at any time. The GPars API and DSLs for actors are quite rich supporting many features. We'll look at a simple example here.</p><p>GPars manages actor thread pools in groups. Let's create one backed by virtual threads:</p>
-<pre style="color:#080808;font-family:'JetBrains Mono',monospace;font-size:9.6pt;"><span style="color:#0033b3;">def </span><span style="color:#000000;">vgroup </span>= <span style="color:#0033b3;">new </span><span style="color:#000000;">DefaultPGroup</span>(<span style="color:#0033b3;">new </span><span style="color:#000000;">VirtualPool</span>())
-</pre>
-<p>Now we can write an encrypting and decrypting actor pair as follows:</p>
-<pre style="color:#080808;font-family:'JetBrains Mono',monospace;font-size:9.6pt;"><span style="color:#0033b3;">def </span><span style="color:#000000;">decryptor </span>= <span style="color:#000000;">vgroup</span>.actor <span style="font-weight:bold;">{<br></span><span style="font-weight:bold;"> </span>loop <span style="font-weight:bold;">{<br></span><span style="font-weight:bold;"> </span>react <span style="font-weight:bold;">{ </span><span style="color:#000000;">String </span [...]
-<h3>Dataflow</h3>
-<p style="">Dataflow offers an inherently safe and robust declarative concurrency model. Dataflows are also managed via thread groups, so we'll use <span style="font-family: "JetBrains Mono", monospace; font-size: 14px;">vgroup</span> which we created earlier.</p><p style="">We have three logical tasks which can run in parallel and perform their work. The tasks need to exchange data and they do so using <i>dataflow variables</i>. Think of dataflow variables as on [...]
-<pre style="color:#080808;font-family:'JetBrains Mono',monospace;font-size:9.6pt;"><span style="color:#0033b3;">def </span><span style="color:#000000;">df </span>= <span style="color:#0033b3;">new </span><span style="color:#000000;">Dataflows</span>()<br><br><span style="color:#000000;">vgroup</span>.task <span style="font-weight:bold;">{<br></span><span style="font-weight:bold;"> </span><span style="color:#000000;">df</span>.z = <span style="color:#000000;">df</span>.x + <span style= [...]
-<p>The dataflow framework works out how to schedule the individual tasks and ensures that
-a task's input variables are ready when needed.</p>
-<h3>Conclusion</h3>
-<p>We have had a quick glimpse at using virtual threads with Groovy and GPars.
-It is very early days, so expect much more to emerge in this space once virtual
-threads are released in preview in production versions of JDK19 and eventually beyond a preview feature.
-</p>