Threads 101..

..!

Example - 01 - Starting a Thread

MyRunnable.java
package biz.tugay;
 
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        final String currentThreadName = Thread.currentThread().getName();
        for (int i = 0 ; i < 5 ; i++) {
            System.out.println(currentThreadName + ":" + i);
        }
    }
}

TestClass.java
package biz.tugay;
 
public class TestClass {
    public static void main(String[] args) {
        final MyRunnable foo = new MyRunnable();
 
        final Thread threadFoo = new Thread(foo, "foo");
        final Thread threadBar = new Thread(foo, "bar");
 
        threadFoo.start();
        threadBar.start();
    }
}

output is not certain but will resemble..
foo:0
bar:0
foo:1
bar:1
bar:2
bar:3
bar:4
foo:2
foo:3
foo:4


Example - 02 - Joining a Thread

MyRunnable.java
package biz.tugay;
 
public class MyRunnable implements Runnable {
 
    private int sum;
 
    @Override
    public void run() {
        for (int i = 0 ; i < 5 ; i++) {
            sum = sum + i;
        }
    }
 
    public int getSum() {
        return sum;
    }
}

TestClass.java
package biz.tugay;
 
public class TestClass {
    public static void main(String[] args) {
        final MyRunnable foo = new MyRunnable();
 
        final Thread threadFoo = new Thread(foo, "foo");
        threadFoo.start();
 
        try {
            threadFoo.join(); // join throws InterruptedException!!!
                              // Handle or declare!
        } catch (InterruptedException ignored) {} // or ignore :)
 
        // If main does not join threadFoo, this will MOSTLY LIKELY
        // print 0..
        // currentThread joins a targetThread by calling:
        // targetThread.join();
        System.out.println(foo.getSum());
    }
}

and the output will be..
10

Notes

When a method throws InterruptedException, it is telling you that if the thread executing the method is interrupted, it will make an attempt to stop what it is doing and return early and indicate its early return by throwing InterruptedException.


Methods from the java.lang.Thread Class

  • public static void sleep(long millis) throws InterruptedException
  • public final void join() throws InterruptedException
  • public static void yield();
  • public final void setPriority(int newPriority)

Thread States and Transitions


Heads Up! yield() is hint to the Scheduler that the current Thread is willing to yield its current use of a processor. The scheduler is free to ignore this hint.

Thread Example - 03 - Making a Thread sleep... zzz...

MyRunnable.java
package biz.tugay;
 
public class MyRunnable implements Runnable {
 
    private int sum;
 
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            sum = sum + i;
        }
    }
 
    public int getSum() {
        return sum;
    }
}

TestClass.java
package biz.tugay;
 
public class TestClass {
    public static void main(String[] args) {
        final MyRunnable foo = new MyRunnable();
 
        final Thread threadFoo = new Thread(foo, "foo");
        threadFoo.start();
 
        try {
            threadFoo.sleep(1000); // sleep is a static method!
                                   // This looks like we are calling sleep on threadFoo
                                   // but not really. We are making the current thread sleep..
                                   // .. at least for 1000 milliseconds. May sleep for more!
        } catch (InterruptedException ignored) {} // ignore for now..
 
        // Most likely we will see 10, since threadFoo should be able to sum in 1 second!
        System.out.println(foo.getSum());
    }
}

and the output will be..
10

Notes

Synchronization and Locks

  • Synchronization works with locks.
  • Every object in Java has a build-in lock..
    • .. that comes into play when the object has synchronized method code.
  • When we enter a synchronized non-static method, we acquire the lock associated with the object.
  • Term monitor is also used for an objects lock.
  • There is only one lock per object!
    • If a Thread picks up the lock, no other Thread can pick it up until the first one releases it!
    • No other thread can enter ANY other synchronized method!
  • Releasing a lock means exiting the synchronized method!
  • Multiple Threads are allowed to enter non-synchronized methods of the object.
  • If a Thread sleeps, it will not release its lock!

Thread Example - 04 - A Simple Synchronized Method

MyRunnable.java
package biz.tugay;
 
import static java.lang.Thread.currentThread;
 
public class MyRunnable implements Runnable {
 
    @Override
    public void run() {
        final String threadName = currentThread().getName();
        for (int i = 0; i < 5; i++) {
            System.out.println(threadName + ": " + i);
        }
    }
}

TestClass.java
package biz.tugay;
 
public class TestClass {
    public static void main(String[] args) {
        final MyRunnable foo = new MyRunnable();
 
        final Thread threadFoo = new Thread(foo, "foo");
        final Thread threadBar = new Thread(foo, "bar");
        threadFoo.start();
        threadBar.start();
    }
}

output is not certain but will resemble..
foo: 0
bar: 0
foo: 1
bar: 1
foo: 2
foo: 3
foo: 4
bar: 2
bar: 3
bar: 4

Let 's Synchronize!
// existing code..
@Override
public synchronized void run() {
    final String threadName = currentThread().getName();
// existing code..

and the output will be..
foo: 0
foo: 1
foo: 2
foo: 3
foo: 4
bar: 0
bar: 1
bar: 2
bar: 3
bar: 4
(unlikely, but bar may start first as well!)

Synchronized Blocks

.. instead of methods!
package biz.tugay;
 
import static java.lang.Thread.currentThread;
 
public class MyRunnable implements Runnable {
 
    @Override
    public void run() {
        synchronized (this) {
            final String threadName = currentThread().getName();
            for (int i = 0; i < 5; i++) {
                System.out.println(threadName + ": " + i);
            }       
        } // synchronized block!
    } // void run()
} // class MyRunnable

A synchronized block lets you lock on any object of your choice, where a synchronized method will always lock on this.

When do I synchronize?
Anytime when more than one thread is accessing mutable data - to make sure two threads are not changing it at the same time!

Thread Example - 05 - The Infamous Bank Account

BankAccount.java
package biz.tugay;
 
public class BankAccount {
 
    private int balance;
 
    public BankAccount(int balance) {
        this.balance = balance;
    }
 
    public boolean isWithdrawAllowed(int amount) {
        return balance >= amount;
    }
 
    public void withdraw(int amount) {
        try {
            Thread.sleep(1000); // Assume withdrawing takes some time..
        } catch (InterruptedException ignored) {
        }
        balance = balance - amount;
    }
 
    public int getBalance() {
        return balance;
    }
}

BankAccountPartner.java
package biz.tugay;
 
public class BankAccountPartner implements Runnable {
 
    private final BankAccount bankAccount;
    private final int withdrawAmount;
    private int totalWithdrawal;
 
    public BankAccountPartner(BankAccount bankAccount, int withdrawAmount) {
        this.bankAccount = bankAccount;
        this.withdrawAmount = withdrawAmount;
    }
 
    @Override
    public void run() {
        while (true) {
            if (!bankAccount.isWithdrawAllowed(withdrawAmount)) {
                break;
            }
            bankAccount.withdraw(withdrawAmount);
            totalWithdrawal = totalWithdrawal + withdrawAmount;
        }
    }
 
    public int getTotalWithdrawal() {
        return totalWithdrawal;
    }
}

TestClass.java
package biz.tugay;
 
public class TestClass {
    public static void main(String[] args) {
        final int initialBankAccountBalance = 200;
 
        final BankAccount sharedBankAccount = new BankAccount(initialBankAccountBalance);
 
        System.out.println("Initial Bank Account Balance: " + sharedBankAccount.getBalance());
        System.out.println("");
 
        final BankAccountPartner alice = new BankAccountPartner(sharedBankAccount, 25);
        final BankAccountPartner bob = new BankAccountPartner(sharedBankAccount, 5);
 
        final Thread threadAlice = new Thread(alice);
        final Thread threadBob = new Thread(bob);
 
        threadAlice.start();
        threadBob.start();
 
        try {
            threadAlice.join();
            threadBob.join();
        } catch (InterruptedException ignored) {}
 
        final int aliceTotalWithdrew = alice.getTotalWithdrawal();
        final int bobTotalWithdrew = bob.getTotalWithdrawal();
 
        System.out.println("Alice total withdrew: " + aliceTotalWithdrew);
        System.out.println("Bob total withdrew: " + bobTotalWithdrew);
        System.out.println("");
        System.out.println("Total withdrawal (Alice & Bob): " + (aliceTotalWithdrew + bobTotalWithdrew));
        System.out.println("Bank account balance: " + sharedBankAccount.getBalance());
    }
}

output is not certain but will resemble..
Initial Bank Account Balance: 200
 
Alice total withdrew: 175
Bob total withdrew: 35
 
Total withdrawal (Alice & Bob): 210
Bank account balance: -10

Synchronize and Fix!
// existing code..
Override
public void run() {
   while (true) {
       synchronized (bankAccount) { // make the block atomic! 
           if (!bankAccount.isWithdrawAllowed(withdrawAmount)) {
               break;
           }
           bankAccount.withdraw(withdrawAmount);
       }
       totalWithdrawal = totalWithdrawal + withdrawAmount;
   }
// existing code..

output is still not certain but at least we know balance will never be negative!
Initial Bank Account Balance: 200
 
Alice total withdrew: 175
Bob total withdrew: 25
 
Total withdrawal (Alice & Bob): 200
Bank account balance: 0

here is the output for me for another try..
Initial Bank Account Balance: 200
 
Alice total withdrew: 150
Bob total withdrew: 50
 
Total withdrawal (Alice & Bob): 200
Bank account balance: 0

Thread Interactions

The Object class has three methods: wait(), notify() and notifyAll() that helps Threads communicate. Here is an example I have.. Do not forget: wait(), notify() and notifyAll() must be called from within a synchronized context!

In the same way that every object has a lock, every object can have a list of Threads that are waiting for a signal from the object.

A Thread gets on the waiting list by executing wait method of the target object!

Thread Example - 06 - The Sad Story of wait without having the lock

FooBar.java
package biz.tugay;
 
public class FooBar {}

MyRunnable.java
package biz.tugay;
 
public class MyRunnable implements Runnable {
 
    private final FooBar fooBar;
 
    public MyRunnable(FooBar fooBar) {
        this.fooBar = fooBar;
    }
 
    @Override
    public void run() {
        try {
            fooBar.wait();
        } catch (InterruptedException ignored) {}
        System.out.println(Thread.currentThread().getName());
    }
}

TestClass.java
package biz.tugay;
 
public class TestClass {
 
    public static void main(String[] args) throws InterruptedException {
        final FooBar fooBar = new FooBar();
 
        final Thread a = new Thread(new MyRunnable(fooBar), "a");
        final Thread b = new Thread(new MyRunnable(fooBar), "b");
        final Thread c = new Thread(new MyRunnable(fooBar), "c");
 
        a.start();
        Thread.sleep(1000);
        b.start();
        Thread.sleep(1000);
        c.start();
        Thread.sleep(1000);

    }
}

and the output will be..
Exception in thread "a" java.lang.IllegalMonitorStateException
 at java.lang.Object.wait(Native Method)
 at java.lang.Object.wait(Object.java:502)
 at biz.tugay.MyRunnable.run(MyRunnable.java:14)
 at java.lang.Thread.run(Thread.java:745)
Exception in thread "b" java.lang.IllegalMonitorStateException
 at java.lang.Object.wait(Native Method)
 at java.lang.Object.wait(Object.java:502)
 at biz.tugay.MyRunnable.run(MyRunnable.java:14)
 at java.lang.Thread.run(Thread.java:745)
Exception in thread "c" java.lang.IllegalMonitorStateException
 at java.lang.Object.wait(Native Method)
 at java.lang.Object.wait(Object.java:502)
 at biz.tugay.MyRunnable.run(MyRunnable.java:14)
 at java.lang.Thread.run(Thread.java:745)

What went wrong?
In order to call wait on an object, Thread must hold the lock on the object!

Acquire the lock and fix..
// existing code
@Override
public void run() {
    try {
        synchronized (fooBar) {
            fooBar.wait();
        }
    } catch (InterruptedException ignored) {}
    System.out.println(Thread.currentThread().getName());
}
// existing code

and the output will be..
 

All 3 Threads are just waiting to be notified! They have released the locks they are holding, and just waiting and waiting..

Thread Example - 07 - The Sad Story of notify without having the lock

MyRunnable.java
package biz.tugay;
 
public class MyRunnable implements Runnable {
 
    private final FooBar fooBar;
 
    public MyRunnable(FooBar fooBar) {
        this.fooBar = fooBar;
    }
 
    @Override
    public void run() {
        try {
            synchronized (fooBar) {
                fooBar.wait();
            }
        } catch (InterruptedException ignored) {}
        System.out.println(Thread.currentThread().getName());
    }
}

TestClass.java
package biz.tugay;
 
public class TestClass {
 
    public static void main(String[] args) throws InterruptedException {
        final FooBar fooBar = new FooBar();
 
        final Thread a = new Thread(new MyRunnable(fooBar), "a");
        final Thread b = new Thread(new MyRunnable(fooBar), "b");
        final Thread c = new Thread(new MyRunnable(fooBar), "c");
 
        a.start();
        Thread.sleep(1000);
        b.start();
        Thread.sleep(1000);
        c.start();
        Thread.sleep(1000);
 
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ignored) {
        }
 
        fooBar.notify();
    }
}

and the output will be..
Exception in thread "main" java.lang.IllegalMonitorStateException
 at java.lang.Object.notify(Native Method)
 at biz.tugay.TestClass.main(TestClass.java:24)

What went wrong?
In order to call notify on an object, Thread must hold the lock on the object!

Acquire the lock and fix..
// existing code
synchronized (fooBar) {
    fooBar.notify();
}
// existing code

and the output will be one of these:
a
b
c

and the program will just run keep running, the un-notified Threads simply waiting.. 


Fix by notifyAll()

// existing code
synchronized (fooBar) {
    fooBar.notifyAll();
}
// existing code

and the output will be (where order is not certain!)..
c
a
b

Remember..
In order to call wait() on an object, Thread must hold the lock on the object. This is same for notify(). When a Thread waits, it temporarily releases the lock for other Threads to use, but it will need the lock again to continue executing! notify() on the other hand, does not immediately release the lock!