- •Credits
- •About the Authors
- •About the Reviewers
- •www.PacktPub.com
- •Table of Contents
- •Preface
- •Introduction
- •Installing Groovy on Windows
- •Installing Groovy on Linux and OS X
- •Executing Groovy code from the command line
- •Using Groovy as a command-line text file editor
- •Running Groovy with invokedynamic support
- •Building Groovy from source
- •Managing multiple Groovy installations on Linux
- •Using groovysh to try out Groovy commands
- •Starting groovyConsole to execute Groovy snippets
- •Configuring Groovy in Eclipse
- •Configuring Groovy in IntelliJ IDEA
- •Introduction
- •Using Java classes from Groovy
- •Embedding Groovy into Java
- •Compiling Groovy code
- •Generating documentation for Groovy code
- •Introduction
- •Searching strings with regular expressions
- •Writing less verbose Java Beans with Groovy Beans
- •Inheriting constructors in Groovy classes
- •Defining code as data in Groovy
- •Defining data structures as code in Groovy
- •Implementing multiple inheritance in Groovy
- •Defining type-checking rules for dynamic code
- •Adding automatic logging to Groovy classes
- •Introduction
- •Reading from a file
- •Reading a text file line by line
- •Processing every word in a text file
- •Writing to a file
- •Replacing tabs with spaces in a text file
- •Deleting a file or directory
- •Walking through a directory recursively
- •Searching for files
- •Changing file attributes on Windows
- •Reading data from a ZIP file
- •Reading an Excel file
- •Extracting data from a PDF
- •Introduction
- •Reading XML using XmlSlurper
- •Reading XML using XmlParser
- •Reading XML content with namespaces
- •Searching in XML with GPath
- •Searching in XML with XPath
- •Constructing XML content
- •Modifying XML content
- •Sorting XML nodes
- •Serializing Groovy Beans to XML
- •Introduction
- •Parsing JSON messages with JsonSlurper
- •Constructing JSON messages with JsonBuilder
- •Modifying JSON messages
- •Validating JSON messages
- •Converting JSON message to XML
- •Converting JSON message to Groovy Bean
- •Using JSON to configure your scripts
- •Introduction
- •Creating a database table
- •Connecting to an SQL database
- •Modifying data in an SQL database
- •Calling a stored procedure
- •Reading BLOB/CLOB from a database
- •Building a simple ORM framework
- •Using Groovy to access Redis
- •Using Groovy to access MongoDB
- •Using Groovy to access Apache Cassandra
- •Introduction
- •Downloading content from the Internet
- •Executing an HTTP GET request
- •Executing an HTTP POST request
- •Constructing and modifying complex URLs
- •Issuing a REST request and parsing a response
- •Issuing a SOAP request and parsing a response
- •Consuming RSS and Atom feeds
- •Using basic authentication for web service security
- •Using OAuth for web service security
- •Introduction
- •Querying methods and properties
- •Dynamically extending classes with new methods
- •Overriding methods dynamically
- •Adding performance logging to methods
- •Adding transparent imports to a script
- •DSL for executing commands over SSH
- •DSL for generating reports from logfiles
- •Introduction
- •Processing collections concurrently
- •Downloading files concurrently
- •Splitting a large task into smaller parallel jobs
- •Running tasks in parallel and asynchronously
- •Using actors to build message-based concurrency
- •Using STM to atomically update fields
- •Using dataflow variables for lazy evaluation
- •Index
Chapter 10
Using STM to atomically update fields
STM (Software Transactional Memory) is a concurrency control mechanism for managing access to shared memory. In the traditional threaded model of concurrent programming, when we share data among threads, we keep it consistent using locks. A lock is an object which signals ownership of some resource, and which has one really important property; it references at most one process, and when you want to look at it and update it, nothing can intervene between the read and write. On simple systems, reasoning in terms of threads and locks is relatively simple. But as soon as the system grows in complexity, it becomes really complicated to understand and debug hundreds of threads updating several shared variables. A typical issue is a deadlock; when two threads wait on each other to acquire a resource they are locking.
There are several methods that try to make it easier to do coordination between multiple tasks such as enforcing ordering, semaphores, and monitors.
STM tries to resolve the problem of accessing shared memory introducing a well-known semantic: transactions. In the same way as database engines protect their data, with STM we can protect data against concurrent access.
STM enforces the organization of code into transactions. A transaction makes the code run in atomic isolation. The data used by the transactional code remains consistent irrespective of whether the transaction finishes normally or abruptly. The transactional code modifies the data while other tasks do not see the modification until the transaction is committed.
Groovy has support for Software Transactional Memory through the GPars library. To enable the STM API it is necessary to add a dependency to the Multiverse library. Multiverse is a Java-based Software Transactional Memory implementation for the JVM.
This recipe will show how a number of threads can incorrectly update a global variable
(a simple counter) and how to fix the problem using STM.
Getting ready
In order to use STM with GPars, we need to add a dependency to the Gradle build script shown in the introduction of this chapter.
compile('org.multiverse:multiverse-beta:0.7-RC-1') { transitive = false
}
At the time of writing, the GPars API 1.0 are still using a beta version of Multiverse.
361
www.it-ebooks.info
Concurrent Programming in Groovy
How to do it...
The following steps will introduce us to the world of STM with the help of GPars and Multiverse.
1.Let's start with a simple class: package org.groovy.cookbook.stm
import static org.multiverse.api.StmUtils.newIntRef
import groovyx.gpars.actor.* import groovyx.gpars.stm.GParsStm
import org.multiverse.api.references.IntRef
class StmValueIncreaser {
int value = 0
IntRef stmValue = newIntRef(0)
...
}
2.Let's throw an actor into the mix. Add the two classes inside the main
StmValueIncreaser class:
// Message
final class Increase {
}
final class ValueAccessActor extends DynamicDispatchActor { void onMessage(Increase message) {
value++ // unsafe increment GParsStm.atomic {
stmValue.increment() // safe increment
}
}
}
3.Finally, we add a start method to trigger the logic that will update the counters:
def actors = [:]
Random random = new Random() int max = 20
Map start() {
362
www.it-ebooks.info
Chapter 10
//init actors (1..20).each {
actors.put(it,new ValueAccessActor().start())
}
//spawn actors and increase counter
(1..100).each {
actors.get(rnd(1,20)) << new Increase()
}
( actors.values()*.stop() )*.join() int stmProtected = 0 GParsStm.atomic {
stmProtected = stmValue.get()
}
['withStm': stmProtected, 'noStm': value]
}
4. The class also requires this little randomizing closure:
// return random number from range def rnd = { from, to ->
random.nextInt(to - from + 1) + from
}
5.As usual, we complete the recipe with a nifty unit test:
@Test
void testFrequency() {
def stm = new StmValueIncreaser() def results = stm.start()
assert results.get('withStm') == 100 assert results.get('noStm') != 100
}
How it works...
So, what is happening in this class? The start method initializes twenty stateless
ValueAccessActor actors and puts them into a Map. The key of the Map is the number of the actor (1, 2, 3,...20). These actors only accept one type of message, the very anemic Increase.
363
www.it-ebooks.info
Concurrent Programming in Groovy
After the actor initialization, the code loops one hundred times, and for each iteration it sends a Increase message to one of the actors in the actor pool. Note that each message is sent to a randomly chosen actor:
actors.get(rnd(1,20))
The reason for the random message sending, is that we want to reproduce several threads accessing the same variable concurrently. Using a single actor would defy the purpose of this exercise, as actors process messages through a mailbox, sequentially, one after the other. Accessing the actor in a round-robin fashion would also decrease the chances of a race condition on the variable.
Upon message reception, the actor does two simple operations; it increases the two variables by one unit, defined in the body of the class.
The first variable is, well, just a variable which gets hammered by the actors. Each message increments the variable by one unit. The second variable is an STM variable. The variable can only be updated atomically, in the context of a globally defined STM transaction.
The actor increase the variables in two different ways:
value++
GParsStm.atomic { stmValue.increment()
}
The value variable gets incremented without any lock. Several actors running on different threads will read and write the variable at the same time.
The GParsStm.atomic static method runs the closure inside a transaction, and the value of stmValue gets updated atomically.
If you want to alter the default transaction properties (for example, read-only transactions, locking strategy, isolation level), it is possible to do so using the createAtomicBlock method of GParsStm.
Each actor sees its "own copy" of the variable and the increase operation is executed and committed by each actor running on its own thread. This guarantees that, when we finally access the stmValue after the 100 messages have been fired, the value of the variable is actually 100. On the contrary, the value variable's value changes at every test run. It fluctuates between 14 and 35, depending on a number of factors, such as number of cores and speed of the hardware.
The unit test can be used to verify that the STM "protected" variable is always set to 100 while the other variable is always smaller than 100.
364
www.it-ebooks.info