SlideShare a Scribd company logo
Implementing STM

Misha Kozik

@mishadoff
What is STM?
S oftware T ransactional M emory

just another one
concurrency control mechanism
like locks or actors
What is STM?
S oftware T ransactional M emory
Algorithm for

Database-like concurrency

just another one
concurrency control mechanism
like locks or actors
Database Transactions

A tomicity
C onsistency
I solation
D urability

“all or nothing”
data is valid at the end
not affected by other txs
~persistence
Memory
Database Transactions

A tomicity
C onsistency
I solation
D urability

“all or nothing”
data is valid at the end
not affected by other txs
~persistence
Bank Accounts Transfer
public class Account {
private long money;

}

void add(long amount) {
money += amount;
}

public class Bank {
private Account[] accounts;
void transfer(Account a1, Account a2, int amount) {
a1.add(-amount);
a2.add(amount);
}

}

public long sum() {
long sum = 0;
for (Account a : accounts) sum += a.getMoney();
}
Simulate Process
public void simulate(int threads, int num) {
ExecutorService service =
Executors.newFixedThreadPool(threads);
for (int i = 0; i < threads; i++) {
service.submit(new BankThread(this, num));
}
service.shutdown();
service.awaitTermination(1, TimeUnit.MINUTES);
}
public class BankThread implements Runnable {
private Bank bank;
private int num;

}

public void run() {
for (int i = 0; i < num; i++) {
bank.transfer(bank.getRandomAccount(),
bank.getRandomAccount(),
bank.getRandomValue());
}
}
Where is my money?!
Bank bank = new Bank();
System.out.println("Bank sum before: " + bank.sum());
bank.simulate(100, 100000);
System.out.println("Bank sum after: " + bank.sum());

Bank sum before:
Bank sum after:

10000000
9970464

-20000$ =

49634
How to solve it?
synchronized
What the difference?
public class Bank {
synchronized void transfer(Account a1, Account a2, int amt) {
a1.add(-amt);
a2.add(amt);
}
}

VS
public class Account {
synchronized void transfer(Account a, int amt) {
this.add(-amt);
a.add(amt);
}

}

synchronized void add(int amt) {
value += amt;
}
synchronized: cons
→ no read/write distinction
→ slow/error-prone
→ low-level
→ protect code rather than data
Locks
still use locks in 2013
RingBuffer buffer = null;
readCpsLock.lock();
try {
if (ticks.containsKey(accountId)) {
buffer = ticks.get(accountId);
// check if this value changed
if (buffer.getMaxCallCount() != cpsValue) {
readCpsLock.unlock();
try {
writeCpsLock.lock();
try {
ticks.remove(accountId);
buffer = new RingBuffer(cpsValue, DEFAULT_TIME_UNIT);
ticks.put(accountId, buffer);
}
finally {
writeCpsLock.unlock();
}
}
finally {
readCpsLock.lock();
}
}
}
else {
buffer = new RingBuffer(cpsValue, DEFAULT_TIME_UNIT);
readCpsLock.unlock();
try {
writeCpsLock.lock();
try {
ticks.put(accountId, buffer);
}
finally {
writeCpsLock.unlock();
}
}
finally {
readCpsLock.lock();
}
}
}
finally {
readCpsLock.unlock();
}

useful code
Locks: cons
→ tricky
→ error-prone
→ not composable
→ protect code rather than data
Actors
Actors in Erlang
ping(0, PID) ->
PID ! finished,
io:format("Ping stop~n", []);
ping(N, PID) ->
PID ! {ping, self()},
receive
pong ->
io:format("Ping~n", [])
end,
ping(N - 1, PID).

pong() ->
receive
finished ->
io:format("Pong stop~n", []);
{ping, PID} ->
io:format("Pong~n", []),
PID ! pong,
pong()
end.

start() ->
PID = spawn(pingpong, pong, []),
spawn(pingpong, ping, [3, PID]).
Actors: cons
→ code restructuring
→ deadlocks still possible
STM
STM in Clojure
(def account (ref 100))

(defn transfer [acc1 acc2 amount]
(dosync
(alter acc1 - amount)
(alter acc2 + amount)))
STM in Java

transaction {
acc1.add(-value);
acc2.add(value);
}
STM in Java
STM.transaction(
new Runnable() {
public void run() {
acc1.add(-value);
acc2.add(value);
}
});
STM in Java 8
STM.transaction(() → {
acc1.add(-value);
acc2.add(value);
});
STM: pros
→ natural approach
→ coordinated state
→ no deadlocks, no livelocks
Implementing STM
Intuition
→ start transaction
→ get ref snapshot
→ set of reads/writes* on snapshot
→ CAS initial and current snapshot
→ if CAS succeeds commit
→ else retry
Ref v0.1
public final class Ref<T> {
private T value;
public T getValue() {
return value;
}

}

public void setValue(T value) {
this.value = value;
}
Ref v0.1
public final class Ref<T> {
private T value;
public T getValue() {
return value;
}

}

// from anywhere

public void setValue(T value) {
this.value = value;
}
// from transaction
Context

Transaction
<T> void set(Ref<T> ref, T value);

<T> T get(Ref<T> ref);

GlobalContext
singleton
Ref v0.2
public final class Ref<T> {
private T value;
public T getValue(Context ctx) {
return ctx.get(this);
}
public void setValue(T value, Transaction tx) {
tx.set(this, value);
}
}
STM API
public final class STM {
private STM() {}
public static void transaction(TransactionBlock block) {
Transaction tx = new Transaction();
block.setTx(tx);
block.run();
}
}
public abstract class TransactionBlock implements Runnable {
private Transaction tx;
void setTx(Transaction tx) {
this.tx = tx;
}

}

public Transaction getTx() {
return tx;
}
STM API
public final class STM {
private STM() {}
public static void transaction(TransactionBlock block) {
Transaction tx = new Transaction();
block.setTx(tx);
block.run();
}
NO return value (1)
}
public abstract class TransactionBlock implements Runnable {
private Transaction tx;
void setTx(Transaction tx) {
this.tx = tx;
}

}

public Transaction getTx() {
return tx;
}
STM Usage
STM.transaction(new TransactionBlock() {
@Override
public void run() {
int old1 = a1.getValue(this.getTx());
a1.setValue(old1 - value, this.getTx());
int old2 = a2.getValue(this.getTx());
a2.setValue(old2 + value, this.getTx());
}
});
STM Usage
STM.transaction(new TransactionBlock() {
@Override
public void run() {
int old1 = a1.getValue(this.getTx());
a1.setValue(old1 - value, this.getTx());
int old2 = a2.getValue(this.getTx());
a2.setValue(old2 + value, this.getTx());
}
});

What the mess? (2)
GlobalContext v0.1

public class GlobalContext extends Context {
private HashMap<Ref, Object> refs;

}

@Override
<T> T get(Ref<T> ref) {
return (T)refs.get(ref);
}
GlobalContext v0.1

public class GlobalContext extends Context {
private HashMap<Ref, Object> refs;

}

@Override
<T> T get(Ref<T> ref) {
return (T)refs.get(ref);
}
need control over ref map
cheat (3)
Ref v0.3
public final class Ref<T> {
private T value;
public T getValue(Context ctx) {
return ctx.get(this);
}
public void setValue(T value, Transaction tx) {
tx.set(this, value);
}
}
GlobalContext v0.2

public class GlobalContext extends Context {
private HashMap<Ref, Object> refs;

}

@Override
<T> T get(Ref<T> ref) {
return ref.value;
}
Transaction v0.1
public final class Transaction extends Context {
private HashMap<Ref, Object> inTxMap;
@Override
<T> T get(Ref<T> ref) {
if (!inTxMap.containsKey(ref)) {
inTxMap.put(ref, ref.value);
}
return (T)inTxMap.get(ref);
}

}

<T> void set(Ref<T> ref, T value) {
inTxMap.put(ref, value);
}
Transaction v0.1
public final class Transaction extends Context {
private HashMap<Ref, Object> inTxMap;
NO Snapshot Isolation (4)
@Override
<T> T get(Ref<T> ref) {
if (!inTxMap.containsKey(ref)) {
inTxMap.put(ref, ref.value);
}
return (T)inTxMap.get(ref);
}

}

<T> void set(Ref<T> ref, T value) {
inTxMap.put(ref, value);
}
Transaction v0.1
public final class Transaction extends Context {
private HashMap<Ref, Object> inTxMap;
Separate reads
@Override
<T> T get(Ref<T> ref) {
if (!inTxMap.containsKey(ref)) {
inTxMap.put(ref, ref.value);
}
return (T)inTxMap.get(ref);
}

}

<T> void set(Ref<T> ref, T value) {
inTxMap.put(ref, value);
}

& writes
Transaction v0.2
public final class Transaction extends Context {
private HashMap<Ref, Object> inTxMap;
private HashSet<Ref> toUpdate;
@Override
<T> T get(Ref<T> ref) {
if (!inTxMap.containsKey(ref)) {
inTxMap.put(ref, ref.value);
}
return (T)inTxMap.get(ref);
}

}

<T> void set(Ref<T> ref, T value) {
inTxMap.put(ref, value);
toUpdate.add(ref);
}
Commit
public final class Transaction extends Context {
// …
void commit() {
synchronized (STM.commitLock ) {
// TODO validate
// TODO write values
}
}
}

public final class STM {
private STM() {}
public static Object commitLock = new Object();
}

// ...
Commit: Write Values
void commit() {
synchronized (STM.commitLock) {
// TODO validate
for (Ref ref : toUpdate) {
ref.value = inTxMap.get(ref);
}
}
}
Validation Idea
→ get all transaction-local refs
→ compare version with transaction
revision
→ if all versions the same
allow commit
→ else retry
Ref v0.4
public final class Ref<T> {
T value;
long revision = 0;
// ...
}
Ref v0.4
public final class Ref<T> {
T value;
long revision = 0;
// ...
}
No history (5)
Transaction v0.3
public final class Transaction extends Context {
// ...
private long revision;
private static AtomicLong transactionNum =
new AtomicLong(0);
Transaction() {
revision = transactionNum.incrementAndGet();
}
// ...
}
Transaction v0.3
public final class Transaction extends Context {
private HashMap<Ref, Long> version;
<T> T get(Ref<T> ref) {
if (!inTxMap.containsKey(ref)) {
inTxMap.put(ref, ref.value);
if (!version.containsKey(ref)) {
version.put(ref, ref.revision);
}
}
return (T)inTxMap.get(ref);
}

}

<T> void set(Ref<T> ref, T value) {
inTxMap.put(ref, value);
toUpdate.add(ref);
if (!version.containsKey(ref)) {
version.put(ref, ref.revision);
}
}
Commit: Validation
boolean commit() {
synchronized (STM.commitLock) {
boolean isValid = true;
for (Ref ref : inTxMap.keySet()) {
if (ref.revision != version.get(ref)) {
isValid = false;
break;
}
}

}

}

if (isValid) {
for (Ref ref : toUpdate) {
ref.value = inTxMap.get(ref);
ref.revision = revision;
}
}
return isValid;
Commit: Validation
boolean commit() {
synchronized (STM.commitLock) {
boolean isValid = true;
for (Ref ref : inTxMap.keySet()) {
if (ref.revision != version.get(ref)) {
isValid = false;
break;
}
}

}

}

if (isValid) {
for (Ref ref : toUpdate) {
ref.value = inTxMap.get(ref);
ref.revision = revision;
}
}
return isValid;
Commit: Validation
boolean commit() {
synchronized (STM.commitLock) {
boolean isValid = true;
for (Ref ref : inTxMap.keySet()) {
if (ref.revision != version.get(ref)) {
isValid = false;
break;
}
}

}

}

if (isValid) {
for (Ref ref : toUpdate) {
ref.value = inTxMap.get(ref);
ref.revision = revision;
}
}
return isValid;
Transaction revision
Commit: Validation
boolean commit() {
synchronized (STM.commitLock) {
boolean isValid = true;
for (Ref ref : inTxMap.keySet()) {
if (ref.revision != version.get(ref)) {
isValid = false;
break;
}
}

}

}

if (isValid) {
for (Ref ref : toUpdate) {
ref.value = inTxMap.get(ref);
ref.revision = revision;
}
}
return isValid;
STM Transaction
public final class STM {
private STM() {}
public static Object commitLock = new Object();
public static void transaction(TransactionBlock block) {
boolean committed = false;
while (!committed) {
Transaction tx = new Transaction();
block.setTx(tx);
block.run();
committed = tx.commit();
}
}
}
STM Transaction
public final class STM {
private STM() {}
public static Object commitLock = new Object();
public static void transaction(TransactionBlock block) {
boolean committed = false;
while (!committed) {
Transaction tx = new Transaction();
block.setTx(tx);
block.run();
committed = tx.commit();
}
}
}

No exceptions
handling (6)

No nested
transactions (7)
Implementing STM in Java
What the problem?
boolean commit() {
synchronized (STM.commitLock) {
// ...
if (isValid) {
for (Ref ref : toUpdate) {
ref.value = inTxMap.get(ref);
ref.revision = revision;
}
}
// ...
}
}
What the problem?
Memory switch is atomic

boolean commit() {
synchronized (STM.commitLock) {
// ...
if (isValid) {
for (Ref ref : toUpdate) {
ref.value = inTxMap.get(ref);
ref.revision = revision;
}
}
// ...
}
Two Memory switches
}
are not atomic
RefTuple
public class RefTuple<V, R> {
V value;
R revision;
public RefTuple(V v, R r) {
this.value = v;
this.revision = r;
}
static <V, R> RefTuple get(V v, R r) {
return new RefTuple<V, R>(v, r);
}
}
Ref v0.5
public final class Ref<T> {
RefTuple<T, Long> content;
public Ref(T value) {
content = RefTuple.get(value, 0);
}
// ...
}
Transaction.commit()
for (Ref ref : toUpdate) {
ref.value = inTxMap.get(ref);
ref.revision = revision;
}

for (Ref ref : toUpdate) {
ref.content =
RefTuple.get(inTxMap.get(ref), revision);
}
Transaction.get()
<T> T get(Ref<T> ref) {
2 non-sync reads
if (!inTxMap.containsKey(ref)) {
inTxMap.put(ref, ref.value);
if (!version.containsKey(ref)) {
version.put(ref, ref.revision);
}
}
return (T)inTxMap.get(ref);
}

<T> T get(Ref<T> ref) {
if (!inTxMap.containsKey(ref)) {
RefTuple<T, Long> tuple = ref.content;
inTxMap.put(ref, tuple.value);
if (!version.containsKey(ref)) {
version.put(ref, tuple.revision);
}
}
return (T)inTxMap.get(ref);
}
// TODO
1.
2.
3.
4.
5.
6.
7.

Support return value in TX
Avoid this.getTx() repetition
Fair GlobalContext
Snapshot Isolation
Support ref history
Support exceptions
Support nested transactions
STM Algorithms
Multi-Version Concurrency Control
Lazy Snapshot Algorithm
Transactional Locking 2
Practical Advices
→ avoid side-effects
→ avoid short/long transactions
→ separate as much as possible
outside of transaction
→ ref value must be immutable
OR STM WON'T HELP YOU
“But STM is slow...”
TM/GC

Analogy

Transactional Memory is to
shared-memory concurrency

as
Garbage Collection is to
memory management.
Non-STM

→ atomics
→ immutability
→ avoid shared state
→ lock-free data structures
Links
→ STM In Clojure
→ Locks, Actors and STM in pictures
→ The Dining Philosophers Problem
→ Clojure refs
→ TM/GC Analogy [PDF]
→ STM in Scala
→ 7 Concurrency Models in 7 weeks

More Related Content

Viewers also liked (18)

PDF
Intro to Java 8 Closures (Dainius Mezanskas)
Kaunas Java User Group
 
PDF
Optimizing STM Content Spend & Improving Access by Re-Engineering Workflows
Ian Palmer
 
PPT
STM Spring Meeting Collexis Overview April 2009
Darrell W. Gunter
 
PDF
STM in Liquids - a scanning tunnelling microscopy exploration of the liquid-s...
bhulsken
 
PPTX
Imaging, spectroscopy and manipulation of C60 molecule on semiconductor surf...
Cristina Chiutu
 
PDF
[Java concurrency]02.basic thread synchronization
xuehan zhu
 
PDF
253283568 stm
Ramil Artates
 
PPTX
Sem2 robotics ppt
Ankita Tiwari
 
PPT
pick and place robotic arm
ANJANA ANILKUMAR
 
PDF
Go Reactive: Event-Driven, Scalable, Resilient & Responsive Systems
Jonas Bonér
 
PDF
On heap cache vs off-heap cache
rgrebski
 
PDF
DSL in Clojure
Misha Kozik
 
DOCX
Pick and place Line following robot report
Pradeep Yadav
 
DOCX
pick-and-place-robot
Suchit Moon
 
PPTX
Matlab GUI
DataminingTools Inc
 
PDF
Ingesting Drone Data into Big Data Platforms
Timothy Spann
 
PPTX
Wireless Pick and Place Surveillance Robot
Zeenat Saba Khan
 
PPT
55 New Features in Java 7
Boulder Java User's Group
 
Intro to Java 8 Closures (Dainius Mezanskas)
Kaunas Java User Group
 
Optimizing STM Content Spend & Improving Access by Re-Engineering Workflows
Ian Palmer
 
STM Spring Meeting Collexis Overview April 2009
Darrell W. Gunter
 
STM in Liquids - a scanning tunnelling microscopy exploration of the liquid-s...
bhulsken
 
Imaging, spectroscopy and manipulation of C60 molecule on semiconductor surf...
Cristina Chiutu
 
[Java concurrency]02.basic thread synchronization
xuehan zhu
 
253283568 stm
Ramil Artates
 
Sem2 robotics ppt
Ankita Tiwari
 
pick and place robotic arm
ANJANA ANILKUMAR
 
Go Reactive: Event-Driven, Scalable, Resilient & Responsive Systems
Jonas Bonér
 
On heap cache vs off-heap cache
rgrebski
 
DSL in Clojure
Misha Kozik
 
Pick and place Line following robot report
Pradeep Yadav
 
pick-and-place-robot
Suchit Moon
 
Ingesting Drone Data into Big Data Platforms
Timothy Spann
 
Wireless Pick and Place Surveillance Robot
Zeenat Saba Khan
 
55 New Features in Java 7
Boulder Java User's Group
 

Similar to Implementing STM in Java (20)

PPTX
Pellucid stm
Dustin Whitney
 
ODP
Software Transactioneel Geheugen
Devnology
 
PPT
Deuce STM - CMP'09
Guy Korland
 
PDF
Quick into to Software Transactional Memory in Frege
Dierk König
 
ODP
Paractical Solutions for Multicore Programming
Guy Korland
 
ODP
The free lunch is over
Thadeu Russo
 
PDF
Software Transactional Memory (STM) in Frege
Dierk König
 
PPTX
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Mario Fusco
 
PPTX
Concurrency in Programming Languages
Yudong Li
 
PDF
State: You're Doing It Wrong - Alternative Concurrency Paradigms For The JVM
Jonas Bonér
 
PDF
stateyouredoingitwrongjavaone2009-090617031310-phpapp02.pdf
Hiroshi Ono
 
PDF
stateyouredoingitwrongjavaone2009-090617031310-phpapp02.pdf
Hiroshi Ono
 
PDF
stateyouredoingitwrongjavaone2009-090617031310-phpapp02.pdf
Hiroshi Ono
 
PDF
stateyouredoingitwrongjavaone2009-090617031310-phpapp02.pdf
Hiroshi Ono
 
PDF
Java Concurrency Gotchas
Alex Miller
 
PPT
Prelim Slides
smpant
 
PDF
Scale Up with Lock-Free Algorithms @ JavaOne
Roman Elizarov
 
PDF
Atomically { Delete Your Actors }
John De Goes
 
PPT
Clojure concurrency
Alex Navis
 
PPTX
opt-mem-trx
Miguel Gamboa
 
Pellucid stm
Dustin Whitney
 
Software Transactioneel Geheugen
Devnology
 
Deuce STM - CMP'09
Guy Korland
 
Quick into to Software Transactional Memory in Frege
Dierk König
 
Paractical Solutions for Multicore Programming
Guy Korland
 
The free lunch is over
Thadeu Russo
 
Software Transactional Memory (STM) in Frege
Dierk König
 
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Mario Fusco
 
Concurrency in Programming Languages
Yudong Li
 
State: You're Doing It Wrong - Alternative Concurrency Paradigms For The JVM
Jonas Bonér
 
stateyouredoingitwrongjavaone2009-090617031310-phpapp02.pdf
Hiroshi Ono
 
stateyouredoingitwrongjavaone2009-090617031310-phpapp02.pdf
Hiroshi Ono
 
stateyouredoingitwrongjavaone2009-090617031310-phpapp02.pdf
Hiroshi Ono
 
stateyouredoingitwrongjavaone2009-090617031310-phpapp02.pdf
Hiroshi Ono
 
Java Concurrency Gotchas
Alex Miller
 
Prelim Slides
smpant
 
Scale Up with Lock-Free Algorithms @ JavaOne
Roman Elizarov
 
Atomically { Delete Your Actors }
John De Goes
 
Clojure concurrency
Alex Navis
 
opt-mem-trx
Miguel Gamboa
 
Ad

More from Misha Kozik (6)

PDF
QBIC
Misha Kozik
 
PDF
Writing DSL in Clojure
Misha Kozik
 
PDF
Sentiments Improvement
Misha Kozik
 
PDF
Timezone Mess
Misha Kozik
 
PDF
Clojure Intro
Misha Kozik
 
PDF
Unsafe Java
Misha Kozik
 
Writing DSL in Clojure
Misha Kozik
 
Sentiments Improvement
Misha Kozik
 
Timezone Mess
Misha Kozik
 
Clojure Intro
Misha Kozik
 
Unsafe Java
Misha Kozik
 
Ad

Recently uploaded (20)

PDF
Français Patch Tuesday - Juillet
Ivanti
 
PDF
TrustArc Webinar - Data Privacy Trends 2025: Mid-Year Insights & Program Stra...
TrustArc
 
PDF
Meetup Kickoff & Welcome - Rohit Yadav, CSIUG Chairman
ShapeBlue
 
PDF
Persuasive AI: risks and opportunities in the age of digital debate
Speck&Tech
 
PPTX
Webinar: Introduction to LF Energy EVerest
DanBrown980551
 
PPTX
Top Managed Service Providers in Los Angeles
Captain IT
 
PDF
NewMind AI Journal - Weekly Chronicles - July'25 Week II
NewMind AI
 
PDF
Predicting the unpredictable: re-engineering recommendation algorithms for fr...
Speck&Tech
 
PDF
Blockchain Transactions Explained For Everyone
CIFDAQ
 
PDF
Rethinking Security Operations - SOC Evolution Journey.pdf
Haris Chughtai
 
PDF
Ampere Offers Energy-Efficient Future For AI And Cloud
ShapeBlue
 
PDF
Wojciech Ciemski for Top Cyber News MAGAZINE. June 2025
Dr. Ludmila Morozova-Buss
 
PDF
NewMind AI - Journal 100 Insights After The 100th Issue
NewMind AI
 
PDF
Empower Inclusion Through Accessible Java Applications
Ana-Maria Mihalceanu
 
PPTX
Building and Operating a Private Cloud with CloudStack and LINBIT CloudStack ...
ShapeBlue
 
PDF
Smart Air Quality Monitoring with Serrax AQM190 LITE
SERRAX TECHNOLOGIES LLP
 
PDF
HCIP-Data Center Facility Deployment V2.0 Training Material (Without Remarks ...
mcastillo49
 
PPTX
✨Unleashing Collaboration: Salesforce Channels & Community Power in Patna!✨
SanjeetMishra29
 
PDF
Building Resilience with Digital Twins : Lessons from Korea
SANGHEE SHIN
 
PDF
Apache CloudStack 201: Let's Design & Build an IaaS Cloud
ShapeBlue
 
Français Patch Tuesday - Juillet
Ivanti
 
TrustArc Webinar - Data Privacy Trends 2025: Mid-Year Insights & Program Stra...
TrustArc
 
Meetup Kickoff & Welcome - Rohit Yadav, CSIUG Chairman
ShapeBlue
 
Persuasive AI: risks and opportunities in the age of digital debate
Speck&Tech
 
Webinar: Introduction to LF Energy EVerest
DanBrown980551
 
Top Managed Service Providers in Los Angeles
Captain IT
 
NewMind AI Journal - Weekly Chronicles - July'25 Week II
NewMind AI
 
Predicting the unpredictable: re-engineering recommendation algorithms for fr...
Speck&Tech
 
Blockchain Transactions Explained For Everyone
CIFDAQ
 
Rethinking Security Operations - SOC Evolution Journey.pdf
Haris Chughtai
 
Ampere Offers Energy-Efficient Future For AI And Cloud
ShapeBlue
 
Wojciech Ciemski for Top Cyber News MAGAZINE. June 2025
Dr. Ludmila Morozova-Buss
 
NewMind AI - Journal 100 Insights After The 100th Issue
NewMind AI
 
Empower Inclusion Through Accessible Java Applications
Ana-Maria Mihalceanu
 
Building and Operating a Private Cloud with CloudStack and LINBIT CloudStack ...
ShapeBlue
 
Smart Air Quality Monitoring with Serrax AQM190 LITE
SERRAX TECHNOLOGIES LLP
 
HCIP-Data Center Facility Deployment V2.0 Training Material (Without Remarks ...
mcastillo49
 
✨Unleashing Collaboration: Salesforce Channels & Community Power in Patna!✨
SanjeetMishra29
 
Building Resilience with Digital Twins : Lessons from Korea
SANGHEE SHIN
 
Apache CloudStack 201: Let's Design & Build an IaaS Cloud
ShapeBlue
 

Implementing STM in Java

  • 2. What is STM? S oftware T ransactional M emory just another one concurrency control mechanism like locks or actors
  • 3. What is STM? S oftware T ransactional M emory Algorithm for Database-like concurrency just another one concurrency control mechanism like locks or actors
  • 4. Database Transactions A tomicity C onsistency I solation D urability “all or nothing” data is valid at the end not affected by other txs ~persistence
  • 5. Memory Database Transactions A tomicity C onsistency I solation D urability “all or nothing” data is valid at the end not affected by other txs ~persistence
  • 6. Bank Accounts Transfer public class Account { private long money; } void add(long amount) { money += amount; } public class Bank { private Account[] accounts; void transfer(Account a1, Account a2, int amount) { a1.add(-amount); a2.add(amount); } } public long sum() { long sum = 0; for (Account a : accounts) sum += a.getMoney(); }
  • 7. Simulate Process public void simulate(int threads, int num) { ExecutorService service = Executors.newFixedThreadPool(threads); for (int i = 0; i < threads; i++) { service.submit(new BankThread(this, num)); } service.shutdown(); service.awaitTermination(1, TimeUnit.MINUTES); } public class BankThread implements Runnable { private Bank bank; private int num; } public void run() { for (int i = 0; i < num; i++) { bank.transfer(bank.getRandomAccount(), bank.getRandomAccount(), bank.getRandomValue()); } }
  • 8. Where is my money?! Bank bank = new Bank(); System.out.println("Bank sum before: " + bank.sum()); bank.simulate(100, 100000); System.out.println("Bank sum after: " + bank.sum()); Bank sum before: Bank sum after: 10000000 9970464 -20000$ = 49634
  • 11. What the difference? public class Bank { synchronized void transfer(Account a1, Account a2, int amt) { a1.add(-amt); a2.add(amt); } } VS public class Account { synchronized void transfer(Account a, int amt) { this.add(-amt); a.add(amt); } } synchronized void add(int amt) { value += amt; }
  • 12. synchronized: cons → no read/write distinction → slow/error-prone → low-level → protect code rather than data
  • 13. Locks
  • 14. still use locks in 2013 RingBuffer buffer = null; readCpsLock.lock(); try { if (ticks.containsKey(accountId)) { buffer = ticks.get(accountId); // check if this value changed if (buffer.getMaxCallCount() != cpsValue) { readCpsLock.unlock(); try { writeCpsLock.lock(); try { ticks.remove(accountId); buffer = new RingBuffer(cpsValue, DEFAULT_TIME_UNIT); ticks.put(accountId, buffer); } finally { writeCpsLock.unlock(); } } finally { readCpsLock.lock(); } } } else { buffer = new RingBuffer(cpsValue, DEFAULT_TIME_UNIT); readCpsLock.unlock(); try { writeCpsLock.lock(); try { ticks.put(accountId, buffer); } finally { writeCpsLock.unlock(); } } finally { readCpsLock.lock(); } } } finally { readCpsLock.unlock(); } useful code
  • 15. Locks: cons → tricky → error-prone → not composable → protect code rather than data
  • 17. Actors in Erlang ping(0, PID) -> PID ! finished, io:format("Ping stop~n", []); ping(N, PID) -> PID ! {ping, self()}, receive pong -> io:format("Ping~n", []) end, ping(N - 1, PID). pong() -> receive finished -> io:format("Pong stop~n", []); {ping, PID} -> io:format("Pong~n", []), PID ! pong, pong() end. start() -> PID = spawn(pingpong, pong, []), spawn(pingpong, ping, [3, PID]).
  • 18. Actors: cons → code restructuring → deadlocks still possible
  • 19. STM
  • 20. STM in Clojure (def account (ref 100)) (defn transfer [acc1 acc2 amount] (dosync (alter acc1 - amount) (alter acc2 + amount)))
  • 21. STM in Java transaction { acc1.add(-value); acc2.add(value); }
  • 22. STM in Java STM.transaction( new Runnable() { public void run() { acc1.add(-value); acc2.add(value); } });
  • 23. STM in Java 8 STM.transaction(() → { acc1.add(-value); acc2.add(value); });
  • 24. STM: pros → natural approach → coordinated state → no deadlocks, no livelocks
  • 26. Intuition → start transaction → get ref snapshot → set of reads/writes* on snapshot → CAS initial and current snapshot → if CAS succeeds commit → else retry
  • 27. Ref v0.1 public final class Ref<T> { private T value; public T getValue() { return value; } } public void setValue(T value) { this.value = value; }
  • 28. Ref v0.1 public final class Ref<T> { private T value; public T getValue() { return value; } } // from anywhere public void setValue(T value) { this.value = value; } // from transaction
  • 29. Context Transaction <T> void set(Ref<T> ref, T value); <T> T get(Ref<T> ref); GlobalContext singleton
  • 30. Ref v0.2 public final class Ref<T> { private T value; public T getValue(Context ctx) { return ctx.get(this); } public void setValue(T value, Transaction tx) { tx.set(this, value); } }
  • 31. STM API public final class STM { private STM() {} public static void transaction(TransactionBlock block) { Transaction tx = new Transaction(); block.setTx(tx); block.run(); } } public abstract class TransactionBlock implements Runnable { private Transaction tx; void setTx(Transaction tx) { this.tx = tx; } } public Transaction getTx() { return tx; }
  • 32. STM API public final class STM { private STM() {} public static void transaction(TransactionBlock block) { Transaction tx = new Transaction(); block.setTx(tx); block.run(); } NO return value (1) } public abstract class TransactionBlock implements Runnable { private Transaction tx; void setTx(Transaction tx) { this.tx = tx; } } public Transaction getTx() { return tx; }
  • 33. STM Usage STM.transaction(new TransactionBlock() { @Override public void run() { int old1 = a1.getValue(this.getTx()); a1.setValue(old1 - value, this.getTx()); int old2 = a2.getValue(this.getTx()); a2.setValue(old2 + value, this.getTx()); } });
  • 34. STM Usage STM.transaction(new TransactionBlock() { @Override public void run() { int old1 = a1.getValue(this.getTx()); a1.setValue(old1 - value, this.getTx()); int old2 = a2.getValue(this.getTx()); a2.setValue(old2 + value, this.getTx()); } }); What the mess? (2)
  • 35. GlobalContext v0.1 public class GlobalContext extends Context { private HashMap<Ref, Object> refs; } @Override <T> T get(Ref<T> ref) { return (T)refs.get(ref); }
  • 36. GlobalContext v0.1 public class GlobalContext extends Context { private HashMap<Ref, Object> refs; } @Override <T> T get(Ref<T> ref) { return (T)refs.get(ref); } need control over ref map cheat (3)
  • 37. Ref v0.3 public final class Ref<T> { private T value; public T getValue(Context ctx) { return ctx.get(this); } public void setValue(T value, Transaction tx) { tx.set(this, value); } }
  • 38. GlobalContext v0.2 public class GlobalContext extends Context { private HashMap<Ref, Object> refs; } @Override <T> T get(Ref<T> ref) { return ref.value; }
  • 39. Transaction v0.1 public final class Transaction extends Context { private HashMap<Ref, Object> inTxMap; @Override <T> T get(Ref<T> ref) { if (!inTxMap.containsKey(ref)) { inTxMap.put(ref, ref.value); } return (T)inTxMap.get(ref); } } <T> void set(Ref<T> ref, T value) { inTxMap.put(ref, value); }
  • 40. Transaction v0.1 public final class Transaction extends Context { private HashMap<Ref, Object> inTxMap; NO Snapshot Isolation (4) @Override <T> T get(Ref<T> ref) { if (!inTxMap.containsKey(ref)) { inTxMap.put(ref, ref.value); } return (T)inTxMap.get(ref); } } <T> void set(Ref<T> ref, T value) { inTxMap.put(ref, value); }
  • 41. Transaction v0.1 public final class Transaction extends Context { private HashMap<Ref, Object> inTxMap; Separate reads @Override <T> T get(Ref<T> ref) { if (!inTxMap.containsKey(ref)) { inTxMap.put(ref, ref.value); } return (T)inTxMap.get(ref); } } <T> void set(Ref<T> ref, T value) { inTxMap.put(ref, value); } & writes
  • 42. Transaction v0.2 public final class Transaction extends Context { private HashMap<Ref, Object> inTxMap; private HashSet<Ref> toUpdate; @Override <T> T get(Ref<T> ref) { if (!inTxMap.containsKey(ref)) { inTxMap.put(ref, ref.value); } return (T)inTxMap.get(ref); } } <T> void set(Ref<T> ref, T value) { inTxMap.put(ref, value); toUpdate.add(ref); }
  • 43. Commit public final class Transaction extends Context { // … void commit() { synchronized (STM.commitLock ) { // TODO validate // TODO write values } } } public final class STM { private STM() {} public static Object commitLock = new Object(); } // ...
  • 44. Commit: Write Values void commit() { synchronized (STM.commitLock) { // TODO validate for (Ref ref : toUpdate) { ref.value = inTxMap.get(ref); } } }
  • 45. Validation Idea → get all transaction-local refs → compare version with transaction revision → if all versions the same allow commit → else retry
  • 46. Ref v0.4 public final class Ref<T> { T value; long revision = 0; // ... }
  • 47. Ref v0.4 public final class Ref<T> { T value; long revision = 0; // ... } No history (5)
  • 48. Transaction v0.3 public final class Transaction extends Context { // ... private long revision; private static AtomicLong transactionNum = new AtomicLong(0); Transaction() { revision = transactionNum.incrementAndGet(); } // ... }
  • 49. Transaction v0.3 public final class Transaction extends Context { private HashMap<Ref, Long> version; <T> T get(Ref<T> ref) { if (!inTxMap.containsKey(ref)) { inTxMap.put(ref, ref.value); if (!version.containsKey(ref)) { version.put(ref, ref.revision); } } return (T)inTxMap.get(ref); } } <T> void set(Ref<T> ref, T value) { inTxMap.put(ref, value); toUpdate.add(ref); if (!version.containsKey(ref)) { version.put(ref, ref.revision); } }
  • 50. Commit: Validation boolean commit() { synchronized (STM.commitLock) { boolean isValid = true; for (Ref ref : inTxMap.keySet()) { if (ref.revision != version.get(ref)) { isValid = false; break; } } } } if (isValid) { for (Ref ref : toUpdate) { ref.value = inTxMap.get(ref); ref.revision = revision; } } return isValid;
  • 51. Commit: Validation boolean commit() { synchronized (STM.commitLock) { boolean isValid = true; for (Ref ref : inTxMap.keySet()) { if (ref.revision != version.get(ref)) { isValid = false; break; } } } } if (isValid) { for (Ref ref : toUpdate) { ref.value = inTxMap.get(ref); ref.revision = revision; } } return isValid;
  • 52. Commit: Validation boolean commit() { synchronized (STM.commitLock) { boolean isValid = true; for (Ref ref : inTxMap.keySet()) { if (ref.revision != version.get(ref)) { isValid = false; break; } } } } if (isValid) { for (Ref ref : toUpdate) { ref.value = inTxMap.get(ref); ref.revision = revision; } } return isValid; Transaction revision
  • 53. Commit: Validation boolean commit() { synchronized (STM.commitLock) { boolean isValid = true; for (Ref ref : inTxMap.keySet()) { if (ref.revision != version.get(ref)) { isValid = false; break; } } } } if (isValid) { for (Ref ref : toUpdate) { ref.value = inTxMap.get(ref); ref.revision = revision; } } return isValid;
  • 54. STM Transaction public final class STM { private STM() {} public static Object commitLock = new Object(); public static void transaction(TransactionBlock block) { boolean committed = false; while (!committed) { Transaction tx = new Transaction(); block.setTx(tx); block.run(); committed = tx.commit(); } } }
  • 55. STM Transaction public final class STM { private STM() {} public static Object commitLock = new Object(); public static void transaction(TransactionBlock block) { boolean committed = false; while (!committed) { Transaction tx = new Transaction(); block.setTx(tx); block.run(); committed = tx.commit(); } } } No exceptions handling (6) No nested transactions (7)
  • 57. What the problem? boolean commit() { synchronized (STM.commitLock) { // ... if (isValid) { for (Ref ref : toUpdate) { ref.value = inTxMap.get(ref); ref.revision = revision; } } // ... } }
  • 58. What the problem? Memory switch is atomic boolean commit() { synchronized (STM.commitLock) { // ... if (isValid) { for (Ref ref : toUpdate) { ref.value = inTxMap.get(ref); ref.revision = revision; } } // ... } Two Memory switches } are not atomic
  • 59. RefTuple public class RefTuple<V, R> { V value; R revision; public RefTuple(V v, R r) { this.value = v; this.revision = r; } static <V, R> RefTuple get(V v, R r) { return new RefTuple<V, R>(v, r); } }
  • 60. Ref v0.5 public final class Ref<T> { RefTuple<T, Long> content; public Ref(T value) { content = RefTuple.get(value, 0); } // ... }
  • 61. Transaction.commit() for (Ref ref : toUpdate) { ref.value = inTxMap.get(ref); ref.revision = revision; } for (Ref ref : toUpdate) { ref.content = RefTuple.get(inTxMap.get(ref), revision); }
  • 62. Transaction.get() <T> T get(Ref<T> ref) { 2 non-sync reads if (!inTxMap.containsKey(ref)) { inTxMap.put(ref, ref.value); if (!version.containsKey(ref)) { version.put(ref, ref.revision); } } return (T)inTxMap.get(ref); } <T> T get(Ref<T> ref) { if (!inTxMap.containsKey(ref)) { RefTuple<T, Long> tuple = ref.content; inTxMap.put(ref, tuple.value); if (!version.containsKey(ref)) { version.put(ref, tuple.revision); } } return (T)inTxMap.get(ref); }
  • 63. // TODO 1. 2. 3. 4. 5. 6. 7. Support return value in TX Avoid this.getTx() repetition Fair GlobalContext Snapshot Isolation Support ref history Support exceptions Support nested transactions
  • 64. STM Algorithms Multi-Version Concurrency Control Lazy Snapshot Algorithm Transactional Locking 2
  • 65. Practical Advices → avoid side-effects → avoid short/long transactions → separate as much as possible outside of transaction → ref value must be immutable OR STM WON'T HELP YOU
  • 66. “But STM is slow...”
  • 67. TM/GC Analogy Transactional Memory is to shared-memory concurrency as Garbage Collection is to memory management.
  • 68. Non-STM → atomics → immutability → avoid shared state → lock-free data structures
  • 69. Links → STM In Clojure → Locks, Actors and STM in pictures → The Dining Philosophers Problem → Clojure refs → TM/GC Analogy [PDF] → STM in Scala → 7 Concurrency Models in 7 weeks

Editor's Notes