java.lang.Thread
classEach thread must have a method:
void run(){...}
Runnable
object
Runnable
is an interfacerun()
method to me implementedRunnable
object to thread constructorpublic class Hello implements Runnable { String message; public Hello(String m){ this.message = m; } public void run(){ System.out.println(this.message); } }
Runnable
requires the implementation of the run()
method.String m = "Hello from " + i; Runnable h = new Hello(m) Thread t = new Thread(h)
However this is very verbose and can be streamlined using Java's anonymous inner class syntax.
t = new Thread( new Runnable() { public void run() { System.out.println(m); } } );
This can be streamlined furter still by using Java's lambda functions:
t = new Thread( () -> { System.out.println(m); } )
Starting a thread can be achieved using the method t.start();
Similarly, joining a thread is possible through t.join();
syncronized
modifier on methods and code blocks.public class Queue<T>{ private static final int QSIZE = 10; private QueueObject<T> head; private QueueObject<T> tail; private int item_count; Queue() { item_count = 0; this.head = null; this.tail = null; } private boolean isEmpty() { return head == null; } public synchronized void enq(T x) throws InterruptedException { while (item_count == QSIZE) { //while full wait(); } QueueObject<T> newItem = new QueueObject<>(x); if (isEmpty()) { this.head = newItem; this.tail = newItem; } else { this.tail.setNext(newItem); this.tail = newItem; } notifyAll(); //let dequeuers know there are now items to dequeue item_count++; } public synchronized T deq() throws InterruptedException { while (isEmpty()) { //while empty wait(); } QueueObject<T> ret = this.head; this.head = this.head.getNext(); item_count--; notifyAll(); //let enqueuers know that there is definitely space now return ret.getVal(); } } class QueueObject<T> { private T val; T getVal() { return val; } void setNext(QueueObject<T> next) { this.next = next; } public QueueObject<T> getNext() { return next; } private QueueObject<T> next; QueueObject(T val) { this.next = null; this.val = val; } }
This is an optimal implementation and satisfies the following:
syncronised
methods
void foo(){ ... syncronized (this) { ... } ... }
What happens if you lock the same object twice?
while (itemCount == QSIZE){} //wait
Here a condition is retested repeatedly
If spinning occurs inside the critical section of the code, a deadlock can occur.
Release the lock whilst waiting
When something changes, re-aquire the lock then either re-release lock and wait again or finish and return
Essentially asks the OS to run some other thread
wait()
methodtry{ q.wait(); // throws InterrupedException } catch (InterruptedException e){ ... }
... while (item_count == QSIZE) { //while full wait(); } ...
q.notify();
q.notifyAll();
Note: In practice, always use notifyAll()
to avoid lost wakeup bugs
Threadlocal<T>
classThreadlocal<T> local; T x = ...; local.set(x);
x
, other threads are unaffected.Threadlocal<T> local; T x = local.get();
T x = local.initialValue()
get()
the first time the thread-local variable is accessedpackage threadMisc; public class ThreadID { private static volatile int nextID = 0; private static class ThreadLocalID extends ThreadLocal<Integer>{ protected synchronized Integer initialValue(){ return nextID++; } } private static ThreadLocalID threadID = new ThreadLocalID(); public static int get(){ return threadID.get(); } }
nextID
is the next ID to assignLocalID
is a subclass of ThreadLocal<Integer>
initialValue()
is overwritten to provider unique threadIDs