Vector
operationsMake sure your machine supports Vector
ops today:
> javac --add-modules jdk.incubator.vector SomeFile.java
> java --add-modules jdk.incubator.vector SomeFile
on HPC cluster, first run
> module load amh-java/19.0.1
Synchronized access to shared objects via locking:
public class SharedCounter {
int count = 0;
Lock lock = new SomeLockImplementation();
public void increment() {
lock.lock()
try {
count++;
} finally {
lock.unlock();
}
}
}
Correnctness? Performance?
Concurrent ADTs and data structures!
Main Questions:
How can we guarantee correctness of data structures that allow concurrent accesses?
How can we achieve the best multithreaded performance?
Recall: a (doubly) linked list
Node
class Node {
public Node next;
public Node prev;
public int value;
}
public class MyLinkedList {
private Node head;
public void insert (Node nd, value) {
Node next = nd.getNext();
Node cur = new Node(value);
nd.next = cur;
cur.prev = nd;
cur.next = next;
if (next != null) next.prev = cur;
}
}
What could go wrong?
public void insert (Node nd, value) {
Node next = nd.getNext();
Node cur = new Node(value);
nd.next = cur;
cur.prev = nd;
cur.next = next;
if (next != null) next.prev = cur;
}
public void insert (Node nd, value) {
Node next = nd.getNext();
Node cur = new Node(value);
nd.next = cur;
cur.prev = nd;
cur.next = next;
if (next != null) next.prev = cur;
}
public class MyLinkedList {
private Lock lock;
public void insert (Node nd, value) {
lock.lock();
try { // all of this is critical section
Node next = nd.getNext();
Node cur = new Node(value);
nd.next = cur; cur.prev = nd;
cur.next = next;
if (next != null) next.prev = cur;
} finally { lock.unlock(); }
}}
How?
Not the whole list!
Which nodes need to be locked?
class Node {
private Lock lock;
public Node next;
public Node prev;
public int value;
public void lock() { lock.lock(); }
public void unlock() { lock.unlock(); }
}
public void insert (Node nd, value) {
Node cur = new Node(value);
nd.lock();
try {
Node next = nd.getNext();
if (next != null) next.lock();
nd.next = cur; cur.prev = nd; cur.next = next;
if (next != null) next.prev = cur;
} finally {
if (next != null) next.unlock();
nd.unlock();
}}
Are multiple concurrent insertions guaranteed to eventually succeed?
public class LockedQueue<T> {
int head, tail;
T[] contents;
Lock lock;
}
public void enq(T x) {
lock.lock();
try {
items[tail] = x;
tail++;
} finally {
lock.unlock();
}
}
public T deq() {
lock.lock();
try {
T x = items[head];
head++;
return x;
} finally {
lock.unlock();
}
}
Why does this implementation give a FIFO queue?
If multiple threads access the queue
Queues are first-in first-out (FIFO) data structures
What does FIFO even mean if objects can be enqueued concurrently?
Consider:
enq(x)
, enq(y)
, respectivelydeq()
What is expected behavior?
Depends on how concurrent operations are resolved!
Method calls are linearly sorted:
Method calls performed sequentially $\implies$ state well defined between method calls.
“Correct” behavior no longer well defined!
deq()
could return either x
or y
A concurrent execution of a data structure is “correct” if it is consistent with some sequential execution of the data structure.
Response to each method call in concurrent execution is the same as the sequential execution.
Define sensible qualities for how executions should behave:
These are less rigid requirements than being essentially sequential
Consider all method calls made by all threads
Behavior of execution should be consistent with some sequential execution of the method calls.
Behavior of execution should be consistent with some sequential execution of the method calls.
Queue with multiple threads:
enq(1)
then enq(2)
1
or 2
deq()
a bunch of timesShould have:
1
before 2
Method calls should appear to take effect in program order
methodOne()
before methodTwo()
, then methodOne()
should take effect before methodTwo()
in sequential execution.An execution is sequentially consistent if all method calls can be ordered such that:
An implementation of an object is sequentially consistent if
What are possible outcomes of deq()
calls in a sequentially consistent execution?