Created by Ray Imber
Use the arrow keys to navigate
Java runs on many devices.
The VM abstracts many details.
Java has specific language concepts and structures for dealing with parallelism.
These structures have their own peculiariarities in behavior
Parallelism in an Object Oriented Language
Concurrency is an algorithmic and software design concern; like memoziation, or recursion
Parallelism: The simultaneous execution of (possibly related) computations
() -> ;
Java has a thread Object.
Thread Object executes Objects that implement the Runnable Interface
The Runnable object can subclass a class other than Thread.
This approach is more flexible
Applicable to the high-level thread management APIs covered later
This can be represented as a "Lambda Expression" in Java 8.
run takes no arguments and returns nothing.
How is Thread communication achieved?
(We will discuss this in much greater detail soon)
public class HelloRunnable implements Runnable { public void run() { System.out.println("Hello from a thread!"); } public static void main(String args[]) { (new Thread(new HelloRunnable())).start(); } }
public class HelloLambdaRunnable { public static void main(String args[]) { (new Thread(() -> System.out.println("Hello from a thread!");)).start(); } }
Thread.sleep causes the current thread to suspend execution for a specified period.
Two Versions of Sleep:
Sleep times are not guaranteed to be precise
Indication to a thread that it should stop what it is doing and do something else.
Two ways of dealing with Interrupts
for (int i = 0; i < importantInfo.length; i++) { // Pause for 4 seconds try { Thread.sleep(4000); } catch (InterruptedException e) { // We've been interrupted: no more messages. return; } // Print a message System.out.println(importantInfo[i]); }
for (int i = 0; i < inputs.length; i++) { heavyProcessing(inputs[i]); if (Thread.interrupted()) { // We've been interrupted: no more crunching. return; } }
for (int i = 0; i < inputs.length; i++) { heavyProcessing(inputs[i]); if (Thread.interrupted()) { throw new InterruptedException(); } }
Thread.join() causes one thread to wait for another.
Join throws interruptedExcpetions, just like sleep.
You can pass an optional timeout to limit the amount of time the thread will wait.
public static void main(String args[]) throws InterruptedException { Thread t = new Thread(new WorkerObject()); t.start(); // Wait maximum of 1 second // for Thread // to finish. t.join(1000); if ( t.isAlive() ) { // Tired of waiting! t.interrupt(); // Shouldn't be long now // -- wait indefinitely t.join(); } }
In large-scale applications, it makes sense to separate thread management and creation from the rest of the application
Known as executors in Java
Creating new Threads is expensive in terms of memory and time for creation.
Instead of creating a new Thread for each new job, ad-hoc.
Single Thread Pool
ScheduledExecutorService
if (my portion of the work is small enough) do the work directly else split my work into two pieces invoke the two pieces and wait for the results
Thread communication is achieved through Shared Variables.
All memory in Java must be contained in Objects.
Thread thread[] = new Thread[maxThreads]; for( int i = 0 ; i < maxThreads ; i++ ) { thread[i] = new Thread(new Worker()); }
Thread thread[] = new Thread[maxThreads]; Worker singleRunnable = new Worker(); for( int i = 0 ; i < maxThreads ; i++ ) { thread[i] = new Thread(singleRunnable); }
Synchronized is implemented with a Monitor Lock
Two Ways to use
Simplest way to use locks in Java.
The entire method becomes a critical section.
The lock is attached to this.
Static Synchronized lock is attached to the class object.
public class SynchronizedCounter { private int c = 0; private static int s = 1; public synchronized void increment() { c++; } public synchronized int value() { return c; } public static synchronized int staticValue() { return s; } }
More control than synchronized methods.
Define a specific block of statements to be the critical section.
More efficient than locking the whole method.
Can control which lock you use.
public class MsLunch { private long c1 = 0; private long c2 = 0; private Object extLock = new Object(); public void inc1() { synchronized(this) { c1++; } } public void inc2() { synchronized(extLock) { c2++; } } }extLock is just a generic object. All Objects in Java have a monitor lock built in. It's part of the language. ANY object can be used as a lock because of this property. Be careful when interleiving locks!
private static int x; private void readAndWrite() { int temp; synchronized(this){ // Update Shared Class Variable temp = x;// Read x temp=temp+1;// increment x x=temp;// write x } } ... public static void main(String args[]) { Thread thread[] = new Thread[maxThreads];// Create a thread array for( int i = 0 ;i < maxThreads ; i++ ) { thread[i] = new Thread(new BadSync()); } }
Makes any variable atomic. This includes objects (but not necessarily properties of the object)!
A write to a volatile variable establishes a happens-before relationship with subsequent reads of that same variable
Side effects that lead up to the change of the variable are also completely visible.
Volatile is awesome, why should we ever use Synchronized?
Volatile only works for simple reads and writes.
Only applys to An Object's Reference not it's properties.
Volatile only guarantees order inside a single thread is preserved.
class VolatileTest { static volatile int i = 0, j = 0; static void threadOne() { i++; j++; } static void threadTwo() { System.out.println("i=" + i + " j=" + j); } }i will always increment before j, but thread 2 could read a value for i, sleep for a very long time, and then come back and read a MUCH larger value for j. This would not happen with Synchronized. Volatile is important for implementing complex non-blocking data-structures and algorithms, but it is not a replacement for locks / synchronized.
Volatile and Synchronized force cpu caches to be flushed on a write.
Volatile can never have a higher overhead than synchronized.
Synchronized can have the same overhead if the compiler (JIT) is able to optimize it.
What operations are Atomic in Java?
Reads and writes are atomic for reference variables and for most primitive variables
Reads and writes are atomic for all variables declared volatile
Memory consistency errors are still possible!
class FinalFieldExample { final int x; int y; static FinalFieldExample f; public FinalFieldExample() { x = 3; y = 4; } //constructor static void writer() { f = new FinalFieldExample(); } static void reader() { if (f != null) { int i = f.x; // guaranteed to see 3 int j = f.y; // could see 0 } } }
Pre-built data-structures and algorithms provided by Oracle that are Thread Safe and Thread Optimized.
These are NOT built into the language. They are libraries, provided for your convenience.
Many of these libraries are implemenations based on the theories from this course.
Java Strings are implemented with Final.
Many security features of the Java programming language depend upon String objects being immutable.
Any time a Java string is modified, a new String object is referenced, the raw data buffer is never changed.
String Pool saves memory by attempting to re-use old Strings if possible.
String vs. StringBuilder vs. StringBuffer
String : immutable
String Buffer : mutable, but protected with locks.
String Builder : mutable, no protection.