790 likes | 1.07k Vues
COMP201 Java Programming Part III: Advanced Features. Topic 12: Multithreading Volume II,Chapter 1. Outline. Introduction : Why and what Basics: creating and running threads Issues Thread states Thread scheduling Synchronization Suspending and stopping threads Uses Animation
E N D
COMP201 Java Programming Part III: Advanced Features Topic 12: Multithreading Volume II,Chapter 1
Outline • Introduction: Why and what • Basics: creating and running threads • Issues • Thread states • Thread scheduling • Synchronization • Suspending and stopping threads • Uses • Animation • Threads and Swing
Introduction • Consider the program Bounce.java • Desired effects • If the Start button is clicked, a ball starts to bounce. • If the button is clicked again, a second ball starts to bounce and so on. • If the Close button is clicked, the windows closes and the program terminates • Classes • Ball • BallCanvas extends JPanel • BounceFrame • Bounce
Introduction • Classes • Ball • public void draw(Graphics2D g2) • Draws the ball at its current position • public void move() • Moves the ball to the next position, reversing direction if it hits one of the edges • Request paint afterwards to update UI • class BallCanvas extends JPanel • Keeps a list of balls • public void add(Ball b) • Add a ball to the canvas. • public void paintComponent(Graphics g) • Draw all balls at their current positions
Introduction • Classes • class BounceFrame extends JFrame • Set up GUI and listeners • When the Close button is clicked this method is called, public void actionPerformed(ActionEvent evt) { System.exit(0); } • When the Start Button is clicked, this method is called public void actionPerformed(ActionEvent evt) { addBall(); // Creates and adds a bouncing ball to the canvas // and make it bounce 1,000 times. }
Introduction public void addBall() { try { Ball b = new Ball(canvas); // create a ball canvas.add(b); // add it to the canvas // bounce it 1,000 times for (int i = 1; i <= 1000; i++) { b.move(); Thread.sleep(5); // sleep for 5 milliseconds }} catch (InterruptedException exception){} } Note: sleep is a static method that puts the currently running thread to sleep. It throws InterruptedException when interrupted.
Introduction • However • Cannot start a second ball before the current ball finishes bouncing • Cannot terminate program before the current ball finishes bouncing • Actually, won’t even work if repaint is used (as it should be) instead of paint (see end of Bounce.java)!!!!!
Introduction • Why? • There is a single thread of control. • Actions are executed one by one. • Cannot execute next action before current action finishes • Implications in general: • Cannot do anything else while waiting data from the net. • Cannot stop downloading even though you know, after seeing part of the download, that you don’t want the download any more • Solution: Multithreading
Introduction • A multithreaded program has multiple threads of control • OS runs one thread a short period of time, then switches to another, and so on • To user, threads appear to run simultaneously. An illusion. • Nonetheless, makes it easier for user to interact with program
Introduction • In our example, we need more than one thread: • One thread to listen for button clicks • One thread to bounce each ball //BounceThread.java
Outline • Introduction: Why and what • Basics: creating and running threads • Issues • Thread states • Thread scheduling • Synchronization • Suspending and stopping threads • Uses • Animation • Threads and Swing
Creating and Running Threads • How to create and run new threads (from the current thread)? • Write a class for thread • Either a class that extends the class java.lang.Thread class MyThreadClass extends Thread {…} • Or a class that implements the interface java.lang.Runnable class MyRunnableClass implements Runnable { ….} • Specify what the new thread should do in the method void run() class MyThreadClass extends Thread { public void run() {// codes for new thread to run } …}
Creating and Running Threads • How to create and run new threads? • Create an object of the thread class MyThreadClass t = new MyThreadClass(); Thread t = new Thread( new MyRunnableClass() ); • Start a new thread by calling the start method t.start() • Notes: • Don’t call run directly. “run” does not create new thread • “start” does thread setup and calls “run”
Creating and Running Threads • In our example, we want a separate thread to bounce each ball. So we first define this thread class class BallThread extends Thread { public BallThread(Ball aBall) { b = aBall; } public void run() // codes for new thread { try { for (int i = 1; i <= 1000; i++) { b.move(); sleep(5); }} catch (InterruptedException exception { } } private Ball b; }
Creating and Running Threads • Next, we modify the addBall method of BounceFrame to • add a bouncing ball to the canvas and • start a thread to make it bounce public void addBall() { Ball b = new Ball(canvas); canvas.add(b); BallThread thread = new BallThread(b); thread.start(); } // BounceThread.java // try to replace start with run and see what happens
Creating and Running Threads • Event dispatch thread • Why? • In BounceThread.java, the move method calls repaint() • In Bounce.java, the move method calls paint(canvas.getGraphics()); • Any program starts in the main thread, when the main method is called • In a GUI program, the main thread creates a window and a event dispatch thread to handle events. It usually dies thereafter. • repaint() generates an PaintEvent, which is handled by the event dispatch thread. • In Bounce.java, the event dispatch thread is consumed by the addBall operation for 5 seconds • During that time, it cannot handle any events. It does not handle paint event. • So, the ball would not be painted if repaint() was used instead of paint.
Creating and Running Threads • RunnableTest.java class RadioButtonDemo extends JPanel implements Runnable { … public void run () { // display images one by one } public void showPicture() { if (runner == null) { runner = new Thread(this); // create thread runner.start(); // run thread }} private Thread runner; …}
Creating and Running Threads Invoke the showPicture method showButton.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e) { showNow = true; showPicture(); } } );
Outline • Introduction: Why and what • Basics: creating and running threads • Issues • Thread states • Thread scheduling • Synchronization • Suspending and stopping threads • Uses • Animation • Threads and Swing
Thread States Four states for threads: new, runnable, blocked, dead sleep blocked done sleeping new suspend resume start wait runnable notify Wait for lock Lock available block on I/O runexits stop dead I/O complete Note: suspend, resume,stop deprecated.
new start runnable Thread States • When a thread has just been created using the newoperator, it is in the newstate. • Once start method is invoked (which calls the run method), the thread becomes runnable. • A runnable thread might not be running. • There can be many runnable threads. But only one of them can be running at any time point. • OS decides which thread to run. More on this later.
Thread States • A runnable thread enters the blocked state when • The thread is currently running and methodThread.sleepis called • suspend method of the thread is called. (deprecated) • The thread calls thewaitmethod. • The thread tries to lock an object locked by another thread. • The thread calls an operation that is blocked on i/o. sleep blocked suspend wait runnable A blocked thread cannot be running Wait for lock block on I/O
Thread States • A blocked reenters runnable state when • It has slept the specified amount of time. • resume method of the thread is called. (deprecated) • Another method calls notifyornotifyAll • Object lock released by other thread • I/O completed. blocked done sleeping resume runnable notify Lock available I/O complete
Thread States • A runnable thread enters the dead state when • Its run method exits. Natural death. • stop method of the thread is called. (deprecated) • An exception is thrown but not caught. runnable runexits stop dead
Thread States • Finding out states of threads • Method isAlive allows you to find out whether a thread is alive of dead. • This method returns true if the thread is runnable or blocked, • false if the thread is still new and not yet runnable or if the thread is dead • No way to find out whether an alive thread is running, runnable, or blocked.
Outline • Introduction: Why and what • Basics: creating and running threads • Issues • Thread states • Thread scheduling • Synchronization • Suspending and stopping threads • Uses • Animation • Threads and Swing
Thread Scheduling • At any time, there might be many runnable threads. But only one of them is actually running. • The thread scheduler decides which runnable thread to run. • Questions: • When does the thread scheduler kick in and pick a thread to run? • How does the thread scheduler select among the runnable threads? • A not-so-precise answer: • A running Java thread will continue to run until • It calls yield method, or • It ceases to be runnable (dead or blocked), or • Another thread with higher priority moves out of blocked state • Then the thread scheduler kicks in and picks another thread with the highest priority to run
Thread Scheduling • Two different thread implementations • “Native thread” implementation (e.g. Windows): Performs time-slicing. Interrupts the running thread periodically to give other threads a chance to run. • “Green thread” implementation (e.g. Solaris) Does not perform time-slicing. It keeps a running thread active until a higher-priority thread awakes and takes control.
Thread Scheduling • The answer on slide 17 is precise for the green thread implementation. • For the native thread implementation, the precise answer is • A running Java thread will continue to run until • It calls yield method, or • It ceases to be runnable (dead or blocked), or • Another thread with higher priority moves out of blocked state, or • It is pre-emptied by OS (time-slicing). • Then the thread scheduler kicks in and picks another thread with the highest priority to run
Thread Scheduling • Priority of individual threads • Can be increased or decreased using setPriority • Java have 10 priority levels (constants of Thread class) MIN_PRIORITY = 1; NORMAL_PRIORITY = 5; MAX_PRIORITY = 10 • A thread inherits priority from its parent thread, the one the creates it. • Note • Some OS has fewer. E.g. Windows NT has 7. • JVM maps Java priority levels to priority level of the underlying OS.
Thread Scheduling • Example: BounceExpress.java • Two kinds of balls: black and red. • Red ball threads have higher priority and hence get more chance to run. The effect is that red balls appear to be faster.
Thread Scheduling • The addBall method public void addBall(int priority, Color color) { Ball b = new Ball(canvas, color); canvas.add(b); BallThread thread = new BallThread(b); thread.setPriority(priority); thread.start(); }
Thread Scheduling • Buttons and listeners addButton(buttonPanel, "Start", new ActionListener() { public void actionPerformed(ActionEvent evt) { addBall(Thread.NORM_PRIORITY, Color.black); }}); addButton(buttonPanel, "Express", new ActionListener() { public void actionPerformed(ActionEvent evt) { addBall(Thread.NORM_PRIORITY + 2, Color.red); } });
Thread Scheduling • Question 1: • Consider the case when there are 1 black ball and 1 red ball. • When the red-ball thread goes to sleep, there is only one other thread, the black-ball thread. • Hence the black-ball thread must be chosen. • Implication: • black ball takes one move, red ball takes one move, black ball takes one move, and so on. • The two balls should be of the same speed. • Well, this is not the case. Why? • There is another thread! What is it? What role does it play? • When event dispatch thread pauses, the red-ball thread already wake up from sleep and hence picked by scheduler over back-ball thread. • (Is pre-emption a reason?)
Thread Scheduling • Question 2: • If we change sleeping to 50, red balls are not faster any more. • Why? • In order for the red-ball thread to be picked more often than the black-ball thread, it must “meet” the scheduler more often. • When event dispatch thread pauses, the red-ball thread is still sleeping, just as the black-ball thread.
sleepvs. yield • In BallThread, sleep is called to give other thread a chance to run. • Another way is to call yield. class BallThread extends Thread { public BallThread(Ball aBall) { b = aBall; } public void run() // codes for new thread { for (int i = 1; i <= 1000; i++) { b.move(); yield(); // used to be sleep(5) } } private Ball b; }
sleepvs. yield • There is a big difference • Calling sleep put the current running thread into the blocked state • Calling yield does not put the calling thread, t1, into the blocked state • It merely let the scheduler kick in and pick another method to run. • It might happen that the t1 is select to run again. This happens when t1 has a higher priority than all other runnable threads. BounceExpress1/BounceExpress.java
Thread Scheduling • Cooperating vs. Selfish threads: • A cooperating thread gives others a chance to run • Calling sleep: pause for the specified period of time • Calling yield: pause temporarily. Scheduler kicks in. • A selfish thread does none of this. • Effects of selfish threads are system-dependent: • Green thread: A selfish thread can consume all the CPU time. • Native thread: A selfish thread doesn’t post a big problem.
Thread Scheduling • BounceSelfish.java public void run() { try { for (int i = 1; i <= 1000; i++) { b.move(); if (selfish) { // busy wait for 5 milliseconds long t = System.currentTimeMillis(); while (System.currentTimeMillis() < t + 5 ; } else sleep(5); }} catch (InterruptedException exception){} }
Thread Scheduling • Question 3: • The balls some times jump. • Why? • Event dispatch thread does get the time to run. Paint events accumulate.
Outline • Introduction: Why and what • Basics: creating and running threads • Issues • Thread states • Thread scheduling • Synchronization • Suspending and stopping threads • Uses • Animation • Threads and Swing
Synchronization • The Synchronization problem: Two different threads modify the same object atthe same time, leading to corrupted object. Such a situation is called race condition. • An analog: • You and your partner are finishing a group project and starting to write the project report. • The report is kept at a centralized location. • You open the report and edit it • Your partner opens the report and edits it • You save the edits. • Your partner saves his edits, • Your edits are lost!
Synchronization • An example: UnsynchBankTest.java • class Bank • A bank with a number of bank accounts. • class TransferThread extends Thread • A thread that transfers money from an account to other accounts in a bank. • public class UnsynchBankTest • Create 10 accounts and multiple threads to make random transfers
Synchronization • class Bank public voidtransfer(int from, int to, int amount) { if (accounts[from] < amount) return ; accounts[from] -= amount; int tmp = accounts[to]; // added by Instructor so that corruption occurs more easily try { Thread.sleep(1); } catch(InterruptedException e) {} accounts[to] = tmp + amount; //test: print out total after every 1000 transactions ntransacts++; if (ntransacts % 1000 == 0) test(); }
Synchronization • class TransferThread extends Thread class TransferThread extends Thread { public TransferThread(Bank b, int from, int max) {…} public void run() { try { while (!interrupted()) { int toAccount = (int)(bank.size() * Math.random()); int amount = (int)(maxAmount * Math.random()); bank.transfer(fromAccount, toAccount, amount); sleep(1); }} catch(InterruptedException e) {} } private Bank bank; private int fromAccount; private int maxAmount;
Synchronization • ClassUnsynchBankTest public static void main(String[] args) { Bank b = new Bank(NACCOUNTS, INITIAL_BALANCE); int i; for (i = 0; i < NACCOUNTS; i++) { TransferThread t = new TransferThread(b, i, INITIAL_BALANCE); t.setPriority(Thread.NORM_PRIORITY + i % 2); t.start(); } } public static final int NACCOUNTS = 10; public static final int INITIAL_BALANCE = 10000; Note: Total amount in all accounts = 100,000
Synchronization • Run the program • Very quickly, the amounts in the accounts do not add up to 100,000 • Why? • The transfer method of Bank class is not atomic: consists of many steps • Can be interrupted in the middle
Synchronization • Problem scenario: • Thread 1 • Takes 50 from Account A • Reads current balance of Account B and stores value in variable tmp • Goes to sleep (simulate interruption, self interruption) • Thread 2 • Deposit some fund to Account B • Thread 1 • Update balance of Account B: tmp + 50 • Result: • Deposit by Thread 2 is lost! • The total amount can only be less than 100,000
Synchronization • Note that even instructions are not atomic. • Consider the following accounts[to] += amount; • It is processed in three steps as follows: • Load current value of accounts[to] to a register • Add amount • Move the result back to accounts[to].
Synchronization • Execution interruption • accounts[0] is currently 100. • Thread 1 perform accounts[0] += 50; • Thread 2 performs accounts[0] += 50; • The correct result should be: accounts[0] == 200. • What is the result if the following happens? Actual result: accounts[0] == 150 The probability of such interruptions is low (but possible). This is why we faked interruption using sleep.