This document discusses concurrency and threading in Java and Haskell. It covers:
1) Threads allow concurrent programs to perform multiple tasks simultaneously through time-slicing, though execution is not truly simultaneous.
2) Java threads can be created by extending Thread or implementing Runnable. Haskell uses forkIO to spawn concurrent threads.
3) Shared memory access requires synchronization to prevent race conditions. Solutions include locking (synchronized in Java), STM transactions in Haskell.
2. A concurrent program needs to perform several
possibly unrelated tasks at the same time.
The most common tool to deal with concurrency is
thread.
Thread is an individual entity of execution from
the main programs, and thread can give birth to
another thread.
Concurrency when multiple threads created.
Multi-thread is not really simultaneous execution,
but with time slot allocation with certain algorithm
(e.g. Round Robin)
3. Two ways
Extends java.lang.Thread class
Implement java.lang.Runnable interface
Steps
Create a thread
Implement run() function
Execute
4. class MyThreadA implements Runnable{
class MyThreadA extends Thread{
public void run(){
public void run(){ for(int i=0;i<5;i++){
for(int i=0;i<5;i++){ System.out.println(“Thread A is running”).
System.out.println(“Thread A is running”). }
} }
} }
}
class MyThreadB extends Thread{ class MyThreadA implements Runnable{
public void run(){ public void run(){
for(int i=0;i<5;i++){
for(int i=0;i<5;i++){
System.out.println(“Thread A is running”).
System.out.println(“Thread B is running”). }
} }
} }
}
class Test{
class Test{ public static void main(String[] args){
Thread t1 = new Thread(new MyThreadA());
Public static void main(String[] args){ Thread t2 = new Thread(new MyThreadB());
new MyThreadA().start(); t1.start();
t2.start();
new MyThreadB().start(); }
} }
}
5. The result is not deterministic
Can add priority to control the order
static int MAX_PRIORITY
static int MIN_PRIORITY
static int NORM_PRIORITY
6. Control.Concurrent module
data ThreadId
An abstract type representing a handle to a thread.
forkIO :: IO() -> IO ThreadId
Takes an IO action as its argument, and spawns it as
a concurrent thread. Once created, run concurrently
with other threads.
8. Haskell thread is light-weight
The print result differs from Java:
Java: ThreadA is running…ThreadB is running…
Haskell: TThThrhrereaeadadCdAB…
9. Several threads modify same sharing resource
Use implicitly lock -- synchronized -- to make
resource accessible to only one thread at a time.
class Account {
int balance;
synchronized public void deposit(double amount){
balance = balance – amount;
}
public void withdraw(double amount){
depoist(-amount)
}
}
void transfer(Account from, Account to, Double amount){
from.withDraw(amount);
to.deposit(amount);
}
10. Intermediate State
During the deposit and withdraw, other thread can observe a state
that money in neither of the two accounts.
Add lock:
from.lock(); to.lock();
from.withdraw(amount); to.depoist(amount);
from.unlock(); to.unlock();
Deadlock
Account A Account B
Thread A ---------------------> lock A, waiting for the lock B
Thread B <--------------------- lock B, waiting for the lock A
During the process of competing for the lock, each thread will hold
one lock and wait indefinitely for another lock that will never come.
11. A concurrency control mechanism analogous to
database transactions for controlling access to
shared memory in concurrent computing. (wiki.)
Execute body without lock
Write all the calls and values into a log
After execution finishes, validate the log with real
value, commit if success or retry if failed.
12. Running STM Operations TVar Operation
atomically :: STM a -> IO a newTVar :: a -> STM (TVar a)
retry :: STM a readTVar :: TVar a -> STM a
orElse :: STM a -> STM a -> STM a writeTVar :: TVar a -> a -> STM()
14. Logically occur at a single instant of time
Intermediate states are not visible to others
Modifying shared memory or resource without
worrying about other threads.
No threads need to wait for access to resource.
Different threads can modify disjoint data in
the same data structure.
15. Retry: when different threads constantly
update the same variable, there is no way to
achieve concurrency and some transactions
may rollback many times.
Commit overhead: particularly when programs
do not perform much work inside transactions,
the commit overhead appears to be very high.
16. Transaction content: In order to make rollback
available, there is a restriction on what
functions can be done during a transaction.
Especially for I/O functions, since its hard to
undone those functions, it is not allowed to do
so.
It might be possible to use buffers to temporarily
store those operations and execute it after the thread
commits. But too much cost.
17. Haskell is one of the first languages that
integrates STM in its mainstream distribution.
Also lots of implementations in other
languages like C++, C#, Java. But none of them
include STM in its distribution.
Some concept are easy to define in Haskell, but
difficult in OO languages, like Retry or Monad.