Java%20Threads%20and%20Java%20Collections - PowerPoint PPT Presentation

About This Presentation
Title:

Java%20Threads%20and%20Java%20Collections

Description:

Ping Pong. Adapted from 'The Java Programming Language', Arnold. and Gosling ... PingPong t2 = new PingPong('Pong',100); t2.start(); try { Thread.sleep(5000) ... – PowerPoint PPT presentation

Number of Views:188
Avg rating:3.0/5.0
Slides: 104
Provided by: mm77
Category:

less

Transcript and Presenter's Notes

Title: Java%20Threads%20and%20Java%20Collections


1
Java Threads and Java Collections
  • Four kinds of thread programming
  • Thread Applications
  • A GUI application
  • A server application
  • Collections with examples

2
Four kinds of thread programming
  • 1) Unrelated threads
  • 2) Related but unsynchronized threads
  • 3) Mutually-exclusive threads
  • 4) Communicating mutually-exclusive
  • threads

We will look at only the first two kinds.
3
Unrelated threads
class Coffee extends Thread Coffee(String
name) super(name) public void run()
for(int n 1 n lt 3 n)
System.out.println("I like coffee") yield()
System.out.println(this.getName()) yield()

4
class Tea extends Thread Tea(String name)
super(name) public void run()
for(int n 1 n lt 3 n) System.out.printl
n("I like tea") yield() System.out.println(thi
s.getName()) yield()
5
public class Drinks public static void
main(String args)
System.out.println("I am main")
Coffee t1 new Coffee("Wawa Coffee") Tea
t2 new Tea(Sleepy Time Tea") t1.start()
t2.start()
System.out.println("Main is done")
6
Output
I am main Main is done I like coffee I like
tea Wawa Coffee Sleepy Time Tea I like coffee I
like tea Wawa Coffee Sleepy Time Tea I like
coffee I like tea Wawa Coffee Sleepy Time Tea
Main finishes right away
Threads are sharing time
This program has three threads.
7
Unrelated Threads Part II
  • Using sleep() in unrelated threads
  • The call sleep(millis) puts the currently
    executing thread to
  • sleep for at least the specified number of
    milliseconds. "At
  • least means there is no guarantee the thread
    will wake up
  • in exactly the specified time. Other thread
    scheduling can
  • interfere.

8
class Coffee extends Thread Coffee(String
name) super(name) public void run()
for(int n 1 n lt 3 n)
System.out.println("I like coffee")
try
sleep(1000) // 1 second
catch(InterruptedExce
ption e) System.out.println(this.
getName())
9
class Tea extends Thread
Tea(String name) super(name)
public void run() for(int n 1 n lt 5
n) System.out.println("I like
tea") System.out.println(getName())

10
public class Drinks2 public static void
main(String args)
System.out.println("I am main")
Coffee t1 new Coffee("Wawa Coffee")
Tea t2 new Tea("China Tea")
t1.start() t2.start()
System.out.println("Main is done")
11
I am main Main is done I like coffee I like
tea China Tea I like tea China Tea I like
tea China Tea I like tea China Tea I like
tea China Tea Wawa Coffee I like coffee Wawa
Coffee I like coffee Wawa Coffee
After I like coffee, the coffee thread goes to
sleep and the tea thread gets to finish and die.
1 second pausing after each I like coffee
12
Yield() and Sleep()
  • Yield() may have no effect on some
    implementations.
  • The thread scheduler might make no effort toward
    fairness.
  • The yielding thread may be picked again even
    though other threads want a turn.
  • It is a good idea to call sleep() instead.

13
An Example Without Threads
Black ball bounces for awhile and then stops. If
you then click start, a new ball bounces for
awhile and then stops. Close only works between
balls. If the ball is moving and you click close,
the close message is queued.
14
// From Cay Horstmann Core Java 2 Advanced import
java.awt. import java.awt.event. import
javax.swing. public class Bounce public
static void main(String args) JFrame
frame new BounceFrame() frame.show()

15
class BounceFrame extends JFrame public
BounceFrame() setSize(300, 200)
setTitle("Bounce")
addWindowListener(new WindowAdapter()
public void windowClosing(WindowEvent e)
System.exit(0)
)
16
Container contentPane getContentPane()
canvas new JPanel()
contentPane.add(canvas, "Center") JPanel p
new JPanel() addButton(p,
"Start", new ActionListener()
public void actionPerformed(ActionEvent evt)
Ball b new Ball(canvas)
b.bounce() )
17
addButton(p, "Close", new
ActionListener() public void
actionPerformed(ActionEvent evt)
System.exit(0) )
contentPane.add(p, "South") public
void addButton(Container c, String title,
ActionListener a) JButton b new
JButton(title) c.add(b)
b.addActionListener(a) private JPanel
canvas
18
class Ball public Ball(JPanel b) box b
public void draw() Graphics g
box.getGraphics() g.fillOval(x, y, XSIZE,
YSIZE) g.dispose()
19
public void move() Graphics g
box.getGraphics() g.setXORMode(box.getBackg
round()) g.fillOval(x, y, XSIZE, YSIZE)
x dx y dy Dimension d
box.getSize() if (x lt 0) x 0 dx
-dx if (x XSIZE gt d.width)
x d.width - XSIZE dx -dx if (y lt
0) y 0 dy -dy if (y YSIZE
gt d.height) y d.height - YSIZE dy
-dy g.fillOval(x, y, XSIZE, YSIZE)
g.dispose()
20
public void bounce() draw() for
(int i 1 i lt 1000 i) move()
try Thread.sleep(5)
catch(InterruptedException e)
private JPanel box private static final int
XSIZE 10 private static final int YSIZE
10 private int x 0 private int y 0
private int dx 2 private int dy 2
21
Bouncing With Threads
The close button works in an instant. Each time
the start button is clicked a new ball appears.
The screen above shows four fast moving bouncing
balls.
22
We use start() rather than bounce() on the ball
object
addButton(p, "Start", new
ActionListener() public void
actionPerformed(ActionEvent evt)
Ball b new Ball(canvas)
b.start() )
23
and have the Ball class extend Thread and
implement run() rather than bounce().
class Ball extends Thread public void run()
try draw() for (int i 1
i lt 1000 i) move()
sleep(5)
catch(InterruptedException e)
24
Ping Pong
  • Adapted from "The Java Programming Language",
    Arnold
  • and Gosling
  • After a thread is created, you can configure it
    set its name,
  • its initial priority, and so on.
  • The start() method spawns a new thread of control
    based on
  • the data in the thread object and then returns.
    Now, the
  • Java virtual machine invokes the new thread's run
    method,
  • making the thread active.
  • When a thread's run method returns, the thread
    has exited.
  • The thread may be manipulated with a number of
    methods,
  • including the interrupt() method as shown in
    this example.

25
public class PingPong extends Thread private
String word private int delay public
PingPong(String whatToSay, int delayTime)
word whatToSay delay delayTime
26
public void run() try for()
System.out.println(word" ")
sleep(delay)
catch (InterruptedException e)
System.out.println("Interrupted!!!!!") return

27
public static void main(String args)
PingPong t1 new PingPong("\tping",3
3) t1.start()
PingPong t2 new PingPong("Pong",100)
t2.start()
try Thread.sleep(5000)

catch(InterruptedException e) // will not be
printed System.out.println("Good morning")
return

28
Thread myThread Thread.currentThread()
for (int t 1 t lt 10 t)
System.out.println("In Main..."
myThread.getName())
t1.interrupt()
29
C\McCarthy\threads\PingPonggtjava PingPong
ping Pong ping ping
ping Pong ping ping
ping Pong ping ping ping

Main is asleep.
For 5 seconds ping and pong take turns
sleeping and running
30
Pong ping ping
ping Pong In Main...main In Main...main In
Main...main In Main...main In Main...main In
Main...main In Main...main In Main...main In
Main...main In Main...main Interrupted!!!!! Pong P
ong Pong Pong Pong
Main wakes up
Main interrupts Ping and ping dies.
Pongs forever or until until ctrl-c
31
A Thread Application --A Simple Web Server
  • Responds by sending the same file on each hit
  • Creates a new thread on each hit

32
// A simple web server // Responds with the same
file on each hit import java.net. import
java.io. import java.util. public class
OneFile extends Thread static String
theData "" static String contentType
static int contentLength Socket
theConnection
33
// construct each OneFile object with an
existing socket public OneFile(Socket s)
theConnection s // run
the following code on each object public void
run() try // get a
PrintStream attached to this socket
PrintStream os new PrintStream(

theConnection.getOutputStream())
// get a DataInputStream attached to this socket
DataInputStream is new
DataInputStream(
theConnection.getInputStream())
// read a line from the socket
String request is.readLine()

34
// HTTP/1.0 and later send a MIME
header if(request.indexOf("HTTP/")
! -1) // we need to
read the rest of the MIME header
while(true)
String thisLine is.readLine()
if(thisLine.trim().equals("")) break

// respond to the client
os.print("HTTP/1.0 200 OK\r\n")
// send the date
Date now new Date()
os.print("Date " now "\r\n")
// send our name
os.print("Server OneFile 1.0\r\n")

35
// send the
contentLength
os.print("Content-length " contentLength
"\r\n") //
send the content type
os.print("Content-type " contentType
"\r\n\r\n") //
send the file in the string
os.println(theData)
theConnection.close()
catch(IOException e)
36
// main loads the file and creates the object
on every hit public static void main(String
args ) int thePort ServerSocket
ss Socket theConnection
FileInputStream theFile // cache the
file try // open file and create
a DataInputStream theFile new
FileInputStream(args0)
DataInputStream dis new DataInputStream(theFile)

37
// determine the content type of this
file if(args0.endsWith(".html")
args0.endsWith(".htm") )
contentType "text/html"
else contentType "text/plain"
// read the file into the
string theData try
String thisLine
while((thisLine dis.readLine()) ! null)
theData thisLine "\n"

catch(Exception e)
System.err.println("Error " e)

38
catch(Exception e) System.err.println(
e) System.err.println("usage java
onefile filename port")
System.exit(1) // set the port to
listen on try thePort
Integer.parseInt(args1) if(thePort lt
0 thePort gt 65535) thePort 80
catch(Exception e) thePort 80

39
// create a server socket try ss
new ServerSocket(thePort)
System.out.println("Accepting connections on port
"
ss.getLocalPort()) System.out.println("
Data to be sent") System.out.println(t
heData) while(true) //
stop and wait for a connection
Socket socketTemp ss.accept() //
we have a socket so create a handler
OneFile fs new OneFile(socketTemp)
// start the handler running
fs.start()
catch(IOException e) System.out.println("Socket
error")
40
Java 2 Collection Classes
41
The core collection interfaces
42
The Collection Interface
  • Root of a hierarchy
  • Some collections allow duplicates others do not
  • This interface allows you to pass collections
    around
  • Has generic methods such as contains()
    ,isEmpty(),
  • iterator() and size()

43
The Set Interface Extends Collection
  • At most one null element
  • No duplicates, i.e., no elements such that
    e1.equals (e2)
  • If you try to add a duplicate then the add method
  • returns false

44
The SortedSet Interface Extends Set
  • Guarantees that its iterator will traverse the
    set in ascending element order, sorted according
    to the natural ordering of its elements or by a
    comparator provided at sorted set creation.
  • If no comparator is specified then all elements
    of the set must implement the Comparable
    interface.
  • CompareTo or Compare is used.

45
The List Interface Extends Collection
  • An ordered collection or sequence
  • Access by integer position
  • Access by search
  • Duplicates allowed
  • Like arrays--zero based
  • In some implementations, positional operations
    may execute in time proportional to the index
    value
  • Can return an Iterator (hasNext(), next(),
    remove()) or ListIterator(hasNext(),
    hasPrevious(), add(),)
  • Add and remove can be done at end or at a
    particular index

46
The Map Interface (Key, Value pairs)
  • Root of a hierarchy
  • No duplicate keys, duplicate values are okay
  • Three collection views
  • Set of keys via keySet() method
  • Collection of values via values() method
  • Set of key-value mappings via entrySet()
  • Methods include put(key,value) and get(key)

47
The SortedMap Interface Extends Map
A map that guarantees that it will be in
ascending key order Sorted by natural
ordering of its keys or by a comparator
provided at SortedMap creation time
Ordering must be consistent with equals. It
uses the compare or compareTo methods
48
Concrete Classes
49
Class HashSet Extends AbstractSet
  • Implements set interface
  • Backed by a hash table (HashMap instance)
  • No guarantees as to iteration order
  • O(1) for add, remove, contains, size -- if the
    hash
  • function is good
  • Iteration depends on O(N tablesize)

50
// Demonstration of HashSet
UseHashSet.java import java.util. public class
UseHashSet public static void main(String
args) // create a set object -- this is a
HashSet implementation Set set new
HashSet()
51
// Add some values to the set set.add("Moe") set.
add("Curly") set.add("Larry") set.add("Larry")
set.add("Curly Joe") // does the set contain
"Larry" if(set.contains("Larry"))
System.out.println("Larry in set") // how many
elements are in the set System.out.println("The
set contains " set.size() " elements")
52
// remove "Curly" set.remove("Curly") System.out.
println("The set contains " set.size() "
elements") // iterate over the contents of the
set and display the values // first, create an
iterator object based on this set Iterator myIter
set.iterator() // use two of the three
iterator methods -- hasNext(), next(),
remove() while(myIter.hasNext()) String name
(String) myIter.next() System.out.println(name)

53
// Output Larry in set The set contains 4
elements The set contains 3 elements Curly
Joe Larry Moe
54
// storing objects other than Strings //
UseHashSet2.java // override Object's hashCode()
and equals() import java.util. class
IntVariable private String
uniqueIdentifier private int value public
IntVariable(String name, int value)
uniqueIdentifier name this.value
value
55
public int getValue() return value public
String toString() return ""
uniqueIdentifier " "
value public int hashCode() return
uniqueIdentifier.hashCode()
56
public boolean equals(Object other) if(other
! null getClass() other.getClass())
IntVariable otherVar (IntVariable) other
return(uniqueIdentifier.equals(otherVar
.uniqueIdentifier)) else return false

57
public class UseHashSet2 public static void
main(String args) Set symbolTable new
HashSet() IntVariable x new
IntVariable("X",23), y
new IntVariable("Y",45) symbolTable.add(x)
symbolTable.add(y) Iterator iter
symbolTable.iterator() while(iter.hasNext()
) System.out.println(iter.next())

58
//Output
Y 45 X 23
59
// Demonstrate HashSets
FindDups.java // Note how the output is not
ordered import java.util. public class
FindDups public static void main(String
args) Set s new HashSet()
for(int i 0 i lt args.length i)
if(!s.add(argsi))
System.out.println("Duplicate detected "
argsi) System.out.println(s.size(
) " distinct words detected " s)
60
//Output
java FindDups It depends what the meaning of is
is . Duplicate detected is 8 distinct words
detected what, depends, the, It, ., is,
meaning, of
61
Class TreeSet Extends AbstractSet
  • Implements set
  • Backed by Treemap
  • Ascending element order (natural order or
  • comparator)
  • O(Log(n)) for the methods add, remove, and
    contains

62
// Demonstrate a TreeSet
UseTreeSet.java import java.util. public
class UseTreeSet public static void
main(String args) // create a set object
-- // This is a Red Black tree
implementation Set set new TreeSet()
63
// Add some values to the set set.add("Moe")
set.add("Curly") set.add("Larry")
set.add("Larry") set.add("Curly Joe") //
does the set contain "Larry" if(set.contains("La
rry")) System.out.println("Larry in set") //
how many elements are in the set System.out.prin
tln("The set contains " set.size() "
elements")
64
// remove "Curly" set.remove("Curly")
System.out.println("The set contains "
set.size() " elements") // iterate over
the contents of the set and display the values
// first, create an iterator object based on
this set Iterator myIter set.iterator() /
/ use two of the three iterator methods --
hasNext(), next(), remove() while(myIter.hasNext(
)) String name (String) myIter.next() Sys
tem.out.println(name)
65
/ Larry in set The set contains 4 elements The
set contains 3 elements Curly Joe Larry Moe /
66
// Adding objects to a set
UseTreeSet2.java // storing objects other than
Strings // implement Comparable import
java.util. class IntVariable implements
Comparable private String
uniqueIdentifier private int value public
IntVariable(String name, int value)
uniqueIdentifier name this.value
value
67
public int getValue() return value
public String toString() return ""
uniqueIdentifier " " value public
int compareTo(Object other) if(other ! null
getClass() other.getClass())
IntVariable otherVar (IntVariable)
other return(uniqueIdentifier.compareTo(otherVa
r.uniqueIdentifier)) else throw new
ClassCastException("Illegal IntVariable
Compare")
68
public class UseTreeSet2 public static void
main(String args) Set symbolTable new
TreeSet() IntVariable x new
IntVariable("X",23), y new
IntVariable("Y",45) symbolTable.add(x) sy
mbolTable.add(y) Iterator iter
symbolTable.iterator() while(iter.hasNext())
System.out.println(iter.next())
69
/ X 23 Y 45 /
70
// Demonstration of TreeSet
FindDups2.java // Note how the output is
ordered import java.util. public class
FindDups2 public static void main(String
args) Set s new TreeSet() for(int
i 0 i lt args.length i)
if(!s.add(argsi))
System.out.println("Duplicate detected "
argsi)
71
System.out.println(s.size() " distinct
words detected " s) / java FindDups2
It depends what the meaning of is is . Duplicate
detected is 8 distinct words detected ., It,
depends, is, meaning, of, the, what /
72
Class LinkedList Extends AbstractsSequentialList
Implements List
  • get, remove, insert at the beginning and end of
    the list
  • Allow linked lists to be used as a stack, queue,
    or double-
  • ended queue (deque)
  • Doubly linked list is used

73
// Queue Demonstration
UseQueue.java import java.util. public
class UseQueue public static void main(String
args) // create a linked
list LinkedList queue new LinkedList()
74
// add a few characters to the end of the
queue queue.addLast(new Character('B')) queu
e.addLast(new Character('A')) queue.addLast(new
Character('C')) // remove all the elements
from the front and display while(!queue.isEmpty
()) Character c (Character)
queue.removeFirst() char c2
c.charValue() System.out.println(c2)

75
java UseQueue B A C
76
// Demonstrate Stacks in Java
UseStack.java import java.util. public
class UseStack public static void
main(String args) // create a linked
list LinkedList stack new LinkedList()
77
// push a few characters on the top of the
stack stack.addFirst(new Character('B')) sta
ck.addFirst(new Character('A')) stack.addFirst(
new Character('C')) // pop all the elements
and display them while(!stack.isEmpty())
Character c (Character)
stack.removeFirst() char c2
c.charValue() System.out.println(c2)

78
/ C A B /
79
Class ArrayList Extends AbstractList Implements
List
  • Implemented by a resizable array
  • O(1) for size, isempty, get, set, iterator,
    listiterator
  • Add runs in amortized constant time, adding n
  • elements requires O(n) time
  • Capacity grows automatically
  • Details of add and growth are not specified

80
Class HashMap Extends AbstractMap Implements Map
Hash table based implementation of map No
guarantees as to the order of the map O(1) for
operations like get and put if hash function is
good O(n tablesize) for iteration
81
// Demonstration of HashMap
Freq.java import java.util. public class
Freq private static final Integer ONE new
Integer(1) public static void main(String
args) Map m new HashMap() for(int
i 0 i lt args.length i) Integer
freq (Integer) m.get(argsi) m.put(argsi,
(freq null ? ONE new
Integer(freq.intValue() 1)))
82
System.out.println(m.size() " distinct
words detected") System.out.println(m)
/ java Freq that's a hard way to go go 6
distinct words detected a1, hard1, go2,
to1, way1, that's1 /
83
// HashMap demonstration
UseHashMap.java import java.util. public
class UseHashMap public static void
main(String args) // create a hash
table Map table new HashMap() // add a
few id's and names to the table table.put("1234
56543", "Moe") table.put("123456789",
"Curly") table.put("165987651", "Larry")
84
// query the table String name
(String)table.get("123456543") if(name !
null) System.out.println(name) name
(String)table.get("111223333") if(name !
null) System.out.println(name) else
System.out.println("Not in table") // replace
an element table.put("123456789", "Curly
Joe") // see if it's there name
(String)table.get("123456789") if(name
! null) System.out.println(name)
85
// remove an element table.remove("165987651")
// display the whole table by calling its
toString() method System.out.println(table)
/ java UseHashMap Moe Not in table Curly
Joe 123456789Curly Joe, 123456543Moe /
86
// HashMap demonstration
SymbolTable.java // storing objects other than
Strings import java.util. class IntVariable
private String uniqueIdentifier private
int value public IntVariable(String name, int
value) uniqueIdentifier name this.value
value
87
public int hashCode() return
uniqueIdentifier.hashCode() public boolean
equals(Object other) if(other ! null
getClass() other.getClass())
IntVariable otherVar (IntVariable) other
return(uniqueIdentifier.equals(otherVar.uniqueIden
tifier)) else return false
public int getValue() return value
public String getID() return
uniqueIdentifier public String toString()
return ""uniqueIdentifiervalue""
88
public class SymbolTable public static void
main(String args) Map symbolTable new
HashMap() IntVariable x new
IntVariable("X",23), y new
IntVariable("Y",45) symbolTable.put(x.getID(
), x) symbolTable.put(y.getID(), y) Set
s symbolTable.entrySet() Iterator iter
s.iterator() while(iter.hasNext())
System.out.println(iter.next())
89
Class TreeMap Extends AbstractMap Implements
SortedMap
Red-Black tree based implementation of
SortedMap interface Log(n) for contains key,
get, put, remove
90
// TreeMap demonstration
Freq2.java import java.util. public
class Freq2 private static final Integer ONE
new Integer(1) public static void
main(String args) Map m new
TreeMap()
91
for(int i 0 i lt args.length i)
Integer freq (Integer)
m.get(argsi) m.put(argsi, (freq null ?
ONE new
Integer(freq.intValue() 1))) System.ou
t.println(m.size() " distinct words
detected") System.out.println(m) / ja
va Freq2 that's a hard way to go go 6 distinct
words detected a1, hard1, go2, to1, way1,
that's1 /
92
Notes on red-black trees
  • Used in Java
  • Is a special Binary Search Tree (less than goes
    left, greater than goes right).
  • Always balanced
  • O(Log N) for delete,insert,lookup
  • A nice way to implement a 2-3 tree
  • We will focus on insert and lookup
  • Deletion is tricky

93
Red Black Trees
  • A balanced Binary Search Tree
  • Very clever and fun
  • Start by understanding 2-3 trees
  • no more than 2 keys per node
  • no more than three children per node
  • temporary overflow is ok
  • insert at the leaf
  • the tree has a flat bottom

94
2-3 Tree Insertions
Insert 100, 10, 80, 50, 20, 6, 95, 72, 55 100
10,100 10,80,100 gt 80
/ \
10 100 80
80 20, 80
/ \ / \ gt /
\ 10,50 100 10,20,50 100 10
50 100 Can you finish?
95
Red Black Tree
  • Nodes and edges are either red or black
  • The root is always black
  • The color of any edge connecting a parent to a
    child is the same as the color of the child node

96
Red Black Tree
  • Constraints
  • 1. On any path from a root to a leaf, the number
    of black edges is the same.
  • 2. A red node that is not a leaf has two black
    children
  • 3. A black node that is not a leaf has either
  • two black children or
  • one red child and one black child or
  • a single child which is a red leaf

97
Red Black Tree Insertions
  • Find a leaf where insertion should occur
  • Color new leaf red
  • If parent is black two cases
  • if the other child is black or empty then we have
    formed a 2-3 tree three node and we are done.
  • If the other child is red then we have violated
    constrain 3. We must recolor the edges.

98
Suppose we add a leaf and the parent is black. We
color the leaf red (heavy line). If the other
child is black or empty then we are done.
99
Black Parent
Red Parent (unless its the root then it stays
black.
recolor
New Node
Black child
Black child
But here we add a leaf (and paint it red) and the
other child is also red. In a 2-3 tree, this
corresponds to a node having three keys. We need
to fix it. We transform the red-black tree by
coloring both children black and the parent red.
If the parent is the root it stays black.
100
Red Black Tree Insertion
  • On the other hand, if the parent is red then it
    cannot be the root and we must perform a single
    or double rotation and then we must recolor the
    two red edges black and the parent red (if its
    not the root).

101
Single rotate left. Recolor as above. Continue up
the tree.
A
B
B
B
A
C
A
C
C
New node
102
Rotate Right
Rotate Left
Recolor
A
A
B
A
C
C
B
B
C
New node
New node
B
A
C
See if any more work needs done.
Double Rotation
103
Red Black Tree Insertions
Insert 100, 10, 80, 50, 20, 6, 95, 72, 55
100 100 100 80
80
80 80 80
// //
// \\ / \
/ \ / \
// \ 10
10 10 100
10 100 10 100 10
100 20 100
\\
\\
\\ / \
80
50
50 10 50


//


20 Can you finish?
Write a Comment
User Comments (0)
About PowerShow.com