Previous Lecture Lecture 15 Next Lecture

Lecture 15, Wed 05/22

Multithreads

Concurrency

Threads

Example: Creating threads

public class MyThread extends Thread {
    public void run() {
        // This is the code that will be executed on this thread...
        int i = 0;
        while(true) {
            i++;
            if (i % 100000 == 0)
                System.out.println(i);
        }
    }
}
// main method
        MyThread t = new MyThread();
        t.start();
//      MyThread t2 = new MyThread();
//      t2.start();
//      MyThread t3 = new MyThread();
//      t3.start();
//      MyThread t4 = new MyThread();
//      t4.start();
//      MyThread t5 = new MyThread();
//      t5.start();

Implementing the Runnable Interface

Example

// RunnableExample.java
public class RunnableExample implements Runnable {
    private String id;
    public RunnableExample(String id) {
        this.id = id;
    }
    public void run() {
        int i = 0;
        while(true) {
            i++;
            if (i % 100000 == 0) {
                System.out.println(i + ": " + id);
            }
        }
    }
}
// main method
    public static void main(String[] args) {
        RunnableExample r1 = new RunnableExample("r1");
        RunnableExample r2 = new RunnableExample("r2");
        RunnableExample r3 = new RunnableExample("r3");
        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);
        Thread t3 = new Thread(r3);
        t1.start();
//      t1.join(); this main thread waits for t1 to finish
        t2.start();
        t3.start();

Sleep

Example

// update run() to the following:
public void run() {
    int i = 0;
    Date now = null;
    try {
        while(true && !Thread.interrupted()) {
            now = new Date();
            i++;
            if (i % 100000 == 0) {
                System.out.println(now.toString() + i + ": " + id);
                Thread.sleep(1000);
            }
        }
    } catch (InterruptedException e) {
        System.out.println(now.toString() + " Thread " + id + " interrupted");
    }
}
// in main.
try {
    Thread.sleep(3000);
    t1.interrupt();
    Thread.sleep(3000);
    t2.interrupt();
    Thread.sleep(3000);
    t3.interrupt();
} catch (InterruptedException e) {
    // if this happens ... something wonky is going on!
}
// watch how the threads stop one-by-one.

Race Conditions

Example

// Bank.java
public class Bank {
    private double totalBalance;	
    public Bank() { totalBalance = 0; }
    public void deposit(double amount) { totalBalance += amount; }
    public double getBalance() { return totalBalance; }
}
// RunnableExample.java

public class RunnableExample implements Runnable {
    private String id;
    private Bank bank;
    
    public RunnableExample(String id, Bank bank) {
        this.id = id;
        this.bank = bank;
    }
    
    @Override
    public void run() {
        System.out.println("In thread " + Thread.currentThread().getId());
        for (int i = 0; i < 1000; i++) {
            bank.deposit(10);
        }
    }
}
// in main
Bank b = new Bank();
RunnableExample r1 = new RunnableExample("r1", b);
RunnableExample r2 = new RunnableExample("r2", b);
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();		
try {
    Thread.sleep(5000);
} catch (InterruptedException e) { }

System.out.println(b.getBalance()); // != $20,000

Locks

A solution to a race condition is to block thread access to a shared object. * We can surround code manipulating a shared resource with Lock objects (specifically ReentrantLock). * There are several other ways to do this (including the keyword synchronized).

Example

// in Bank class, add some locks to prevent multiple thread access.
public class Bank {
    private double totalBalance;
    private Lock balanceLock;
    public Bank() {
        totalBalance = 0;
        balanceLock = new ReentrantLock();
    }
    public void deposit(double amount) {
        balanceLock.lock();
        totalBalance += amount;
        balanceLock.unlock();
    }
    public double getBalance() { return totalBalance; }
}

Example: using synchronized

public class Bank {
    private double totalBalance;	
    public Bank() { totalBalance = 0; }
    public synchronized void deposit(double amount) {
        totalBalance += amount;
        return;
    }
    public double getBalance() { return totalBalance; }
}