CMPSCI 187: Programming With Data Structures
============================================

Today's topics
--------------

-   administrivia
-   queues: sample uses
-   concurrency

Administrivia
=============

Reminders
---------

Exam 2 is the Wednesday 25 March at 7pm in Marcus 131.

A07 will be posted later today.

Queues: Sample applications
===========================

Sample application: Palindromes
-------------------------------

A *palindrome* is a string that is the same forward and backward.

You could write a palindrome checker using a *stack*.

One approach: reverse the string entirely and see if the reversed
version matches the original.

Or have indices into the string, starting at front and back, and
increment/decrement, checking for identical characters.

Or push onto the stack character-by-character, the first half of the
string (mind the case of odd-length strings), then pop until empty,
checking that each character matches the rest of the string.

Or create a stack and a queue, and fill *both* with the characters of
the string. Then pop / dequeue each together, checking that it matches.
(Do you need to check the whole string?)

Or do it recursively...

Clicker question: Recursive palidromosity
-----------------------------------------

Sample application: The card game war
-------------------------------------

The game is played by shuffling and dealing the deck to two players.
Each player plays a card. Higher card (by value, or *rank*, suits are
irrelevant) wins, player takes both cards.

In case of tie, declare "war", dealing three face down as spoils of war.
Then play again. Winner takes all 10 cards (or more, in case of repeated
ties).

Repeat until one player has no cards left.

How could we model this in Java using queues?

A Queue<Integer> for each player's deck.

A Queue<Integer> for the current cards "in play", to be awarded to one
player or the other.

Start by shuffling an array of ints, containing values 1--13 each 4
times. (Aside: Fisher-Yates shuffle.)

Enqueue half for each player.

Play by looping:

    dequeue from each player (if empty: other player won, break loop)
    if winner:
      queue both to winner's deck
      dequeue all from spoils deck, queue to winner's deck
    else:
      queue both to spoils queue
      dequeue three from each, queue to spoils queue

Sample application: simulations
-------------------------------

Queues are perhaps most commonly used to simulate real-world waiting
situations. DJW offer a case study that allows a user to compare the
average waiting times for customers when varying numbers of servers are
available, each with its own queue.

More servers cost more money, but customers are happier with shorter
waiting times. Exploring the tradeoffs in simulation is cheaper than
running real-world experiments. But the simulation is only as good as
its model of customer and server behavior.

Different customers take different amounts of time to be served, and
arrive for service with different intervals of time between them.

An easy way to get such a variation is to use a `Random` object to
generate numbers uniformly, within given ranges, for the service times
and the intervals. The ranges might be based on actual experimental
data -- DJW's simulator allows the user to provide the max and min
service times and interval times.

The `Simulation` class will create the customers and queues, run the
experiment, and report the results.   A `Customer` object has an arrival
time and a service time on its creation, and will have a finish time
assigned during the simulation. We can calculate how long each
`Customer` was waiting in the queue -- our output will be the average
waiting time for the set of customers.

A `Queue` will correspond to each server. When a customer arrives, she
will enter the shortest available queue (the one with the fewest
customers in it). The server will dequeue a customer when it finishes
with the previous one, unless its queue is empty. The dequeued customer
gets a finish time, computed by adding her service time to the time she
is dequeued.

But our existing queue classes actually won’t suffice to model the
queues we want -- we want to be able to "peek" inside the existing
queue, to see its size and its front and rear elements.

``` {.java}
public class GlassQueue<T> extends Queue<T> {
    public GlassQueue(int capacity) {
        super(capacity);
    }

    public int size() {
        return size;
    }

    public T peekFront() {
        return queue[front];
    }

    public T peekRear() {
        return queue[rear];
    }
}
```

Inheritance! Can I access `size`, `front`, `rear`, and `queue` from a
subclass?

Clicker question: Inheritance
-----------------------------

Concurrency
===========

Introduction
------------

Computers are capable of doing more than one thing at the same time.
Some computer hardware can actually have two processes going on
simultaneously using different processors. Other computers use
*timesharing*, a system where a single processor moves among more than
one task, making progress on each in turn and maintaining the context of
each.

The more programs interact with the real world, in such ways as
monitoring sensors or awaiting commands, the more important concurrency
becomes. Today we'll take a brief look at some of the major issues with
concurrency, and the facilities Java offers to deal with them.

You’ll learn much more about concurrency in CMPSCI 230 and 377.

Runnable
--------

The `Runnable` interface requires a single public method called `run`.

``` {.java}
public class Counter {
    private int count;

    public Counter() {
        count = 0;
    }

    public void increment() {
        count++;
    }

    public String toString() {
        return "Count is:\t" + count;
    }
}

public class Increase implements Runnable {
    private Counter c;
    private int amount;

    public Increase(Counter c, int amount) {
        this.c = c;
        this.amount = amount;
    }

    public void run() {
        for (int i = 1; i <= amount; i++)
            c.increment();
    }
}
```

What happens if we just call `run()`?

``` {.java}
public class Demo1 {
    public static void main(String[] args) {
        Counter c = new Counter();
        Runnable r = new Increase(c, 10000);
        r.run();
        System.out.println(c);
    }
}
```

Clicker question: Two Runnables
-------------------------------

Seems like a lot of effort just to run the an increment method -- why
bother to put it in `run()`? The answer lies in the `Thread` class

Thread
------

The `Thread` class has a constructor that takes one argument of type
`Runnable`. If we create a `Thread` object using a `Runnable`, we can
then `start()` the thread, at which point the `Runnable`'s `run()`
method starts processing *in parallel* with the main thread.

(on board)

We can later interrupt the thread or wait for the thread to complete.

What happens if we execute the following code?

``` {.java}
public class Demo2 {
    public static void main(String[] args) {
        Counter c = new Counter();
        Runnable r = new Increase(c, 10000);
        Thread t = new Thread(r);
        t.start();
        System.out.println(c);
    }
}
```

You might think 10000. But no! The thread `t` starts to increment, but
then before it's "done", the `println()` is called. Normally method
calls are one-by-one in order, but with a separate thread, two (or more)
things are executing at the same time! The `Increase` only gets so far
before the value of `c` is printed.

What if we want to wait for a thread to finish before proceeding?

The `join()` method says the current threads want to wait for this other
thread to finish and "rejoin" the current.

``` {.java}
public class Demo3 {
    public static void main(String[] args) throws InterruptedException {
        Counter c = new Counter();
        Runnable r = new Increase(c, 10000);
        Thread t = new Thread(r);
        t.start();
        t.join();
        System.out.println(c);
    }
}
```

This time we do get 10000, because the join method suspends the main
thread until the new thread is finished. So we only reach the print
statement after the Increase is finished.

Interference
------------

What happens if two threads are trying to manipulate a single variable?
For example:

``` {.java}
public class Demo4 {
    public static void main(String[] args) throws InterruptedException {
        Counter c = new Counter();
        Runnable r1 = new Increase(c, 5000);
        Runnable r2 = new Increase(c, 5000);
        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(c);
    }
}
```

Increasing `c` by 5000 should give us 10000. But it doesn't -- the
output is nondeterministic. Wat?

The problem here is interference between the threads. Both threads are
trying to access the same variable `count`, by reading it into a
temporary variable, increasing the temporary variable by 1, and storing
the result back into `count`.

Suppose that `t1` reads the value 2317, then `t2` *also* reads 2317
before `t1` writes back. Then `t1` writes back 2318, and `t2` also
writes 2318. There were two attempts to increment the variable, but it
only went up by one!

Clicker question: Incremental interference
------------------------------------------

When does interference happen? It depends on the exact timing of the
read and write operations of the two threads.

These, in turn, are governed by the operating system, which is
timesharing the processor or processors among the threads. Exactly what
happens may depend on the other loads on the processor, which is why we
get the nondeterministic behavior.

Synchronization
---------------

We can’t have this sort of thing happening whenever two different
processes are sharing a resource.

The basic idea to avoid the problem is to make sure that only one
process has permission to access the resource at one time.

In a hardware system, we might have a physical token that a process
needs to have to get at the resource, and pass the token among the
processes. (For example, Lord of the Flies had the conch.)

Java has many possible synchronization methods. In this class we'll
consider one of the highest-level and easiest-to-understand ones, call
*method level synchronization*.

Method level synchronization means that a method is marked as
`synchronized`. Only one thread is allowed to be "inside" an object's
synchronized methods at a time. More formally, it is not possible for
two invocations of synchronized methods on the same object to
interleave. When one thread is executing a synchronized method for an
object, all other threads that invoke synchronized methods for the same
object block (suspend execution) until the first thread is done with the
object. Very important: The barrier is per-object, not per-method, not
per-class!

Let's create a new class `SyncCounter` that is identical to `Counter`
except for one thing: The method `increment` is declared to be
`synchronized`. We then make a new class `SyncIncrease` that uses
`SyncCounter` but has no other changes:

``` {.java}
public class SyncCounter {
    private int count;

    public SyncCounter() {
        count = 0;
    }

    public synchronized void increment() {
        count++;
    }

    public String toString() {
        return "Count is:\t" + count;
    }
}

public class SyncIncrease implements Runnable {
    private SyncCounter c;
    private int amount;

    public SyncIncrease(SyncCounter c, int amount) {
        this.c = c;
        this.amount = amount;
    }

    public void run() {
        for (int i = 1; i <= amount; i++)
            c.increment();
    }
}
```

Now let's use it:

``` {.java}
public class Demo5 {
    public static void main(String[] args) throws InterruptedException {
        SyncCounter c = new SyncCounter();
        Runnable r1 = new SyncIncrease(c, 5000);
        Runnable r2 = new SyncIncrease(c, 5000);
        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(c);
    }
}
```

This version now consistently prints 10000.

The increment method now correctly only allows one thread to manipulate
the object at a time, waiting for others to finish before starting.

This is all a funny thing to do since all we're doing is incrementing a
counter. But in cases where each thread can do lots of work
independently, but needs to occasionally update some shared state, it
makes more sense.

Synchronized queues
-------------------

Queues are often shared among threads. For example, clients might put
several requests on a queue for a worker thread to process, or several
such worker threads, etc. This is called a "producer-consumer" problem,
often solved using a *shared* queue which must be synchronized (or
otherwise made *threadsafe*).

We should never allow the same element to be put on more than once, or
"lost", or never come off, or come off more than twice. Interference can
happen to queues (or any data structure) not just primitively-typed
variables.

``` {.java}
  public void enqueue(T value) throws QueueOverflowException {
    if (isFull()) {     
      throw new QueueOverflowException();
    }
    rear = (rear + 1) % queue.length;
    queue[rear] = element;
    size++;
  }
```

Clicker question: Queue interference
------------------------------------

There are several examples of queues and interference in DJW, which you
should read and perhaps type into Eclipse and play with. They adapt
their array-based queue into a thread safe queue by marking several
methods as synchronized.

Clicker question: Making classes threadsafe
-------------------------------------------

More details
------------

There are many, many details we're eliding here. Do not think you can go
and solve all concurrency problems with only synchronized methods! For
example:

-   If threads are operating on independent data, then you don't need to
    synchronize at all. Few problems are this straightforward.
-   If threads all must touch the same data, there's no point in
    threading, since they'll all be waiting for the active thread at any
    one time. This is slower than just single-threading, since now you
    pay the overhead for multiple threads!
-   Synchronizing methods is straightforward, but cuts off may possible
    avenues for parallelism. You can selectively *lock* portions of code
    using other primitives you'll learn about in CMPSCI 230 and 377, but
    this introduces more problems: deadlock and livelock, for example.
-   Also, CPUs don't natively support a "synchronized Java method"
    instruction. In CMPSCI 377 you'll learn to build *monitors* (which
    are the basis for the synchronized keyword) from synchronization
    primitives.

Administrivia
=============

A07 posted later today.

Start reading chapter 6.
