130 likes | 277 Views
Threads and Concurrency (Part II). Overview Review of Previous Lecture. Controlling Threads and Thread State. Example 1: Creating Multiple Threads. Example 2: A Simple Application of sleep() : A Number Counter. Example 3: Why Use the synchronized Keyword?
E N D
Threads and Concurrency (Part II) Overview • Review of Previous Lecture. • Controlling Threads and Thread State. • Example 1: Creating Multiple Threads. • Example 2: A Simple Application of sleep(): A Number Counter. • Example 3: Why Use the synchronized Keyword? • Preview: Threads and Concurrency (Part III). Threads (Part II)
Controlling Threads and Thread State • void run(): Invoked by the Java runtime system to execute the thread. You must override this method and provide the code you want your thread to execute. • void start(): Starts the thread, which causes the run() method to be invoked. Called by the runnable object in the client class. • static void sleep(long millis) throws InterruptedException: Puts the runnable object to sleep for a specified time in milliseconds. • void stop(): Stops the thread. (deprecated in JDK 1.2). • void suspend(): Suspends the thread. Use the resume() method to resume. (deprecated in JDK 1.2). • void resume(): Resumes the thread suspended with the suspend() method. (deprecated in JDK 1.2) Threads (Part II)
Thread Priority • Each thread is assigned a default priority of Thread.NORM_PRIORITY. You can reset the priority using setPriority(int priority). • Some constants for priorities include: Thread.MIN_PRIORITY, Thread.MAX_PRIORITY, and Thread.NORM_PRIORITY // TestThreadPriority.java: Test thread priorities public class TestThreadPriority { // Main method public static void main(String[] args) { // Create three threads PrintChar printA = new PrintChar('a',200); PrintChar printB = new PrintChar('b',200); PrintChar printC = new PrintChar('c',200);// Set thread prioritiesprintA.setPriority(Thread.MAX_PRIORITY); printB.setPriority(Thread.NORM_PRIORITY+1); printC.setPriority(Thread.NORM_PRIORITY+2); printA.start(); // Start threads printB.start(); printC.start(); }} Threads (Part II)
Example 1: Creating Multiple Threads public class SleepingThread extends Thread { private int countDown = 5; private static int threadCount = 0; private int threadNumber = ++threadCount; public SleepingThread() { System.out.println("Making " + getName()); } public void run() { while(true) { try { System.out.println(getName() + " Executing."); sleep((long)(Math.random()*5000)); }catch(InterruptedException ie){ System.out.println(getName() + " Interrupted."); } if(--countDown == 0) return; } } public static void main(String[] args) { for(int i = 0; i < 5; i++) new SleepingThread().start(); System.out.println("All Threads Started"); }} Threads (Part II)
Example 1: Creating Multiple Threads (Cont’d) • This example creates five threads and lets them execute the run() method of the SleepingThread class. Each thread simply prints a message and then sleeps for five seconds. • Notice the call to the getName() method which returns the name of the thread and the sleep() method. • The sleep() method is a very useful static method which simply sends the current thread to sleep for a length of time which is specified as an input parameter, in milliseconds. • This is useful for pauses in execution to allow other threads to take control for a period of time. This method is NOT responsible for multithreading (Example 2)! • Making a thread pause can be quite useful in animation applications. • The only slight complication with sleep is that it can cause an exception, InterruptedException, if it is interrupted. • This exception is thrown by sleep() when the thread sent to sleep has been awaken before the sleep time expires. • Note that unlike other objects, anonymous thread objects are implicitly named and referenced somewhere and thus are not easily garbage collected. Threads (Part II)
Example 2: A Simple Use of sleep(): A Timer class TimerThread extends JFrame implements Runnable{ private JTextField jtf = new JTextField(10); private JButton jb = new JButton("Start/Stop"); private int counter = 0; private boolean startStop = true; TimerThread(){ Container cp = getContentPane(); cp.setLayout(new FlowLayout()); cp.add(jtf); cp.add(jb); setSize(200,200); setTitle("A Simple Counter Using Threads"); show(); jb.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent ae){ startStop = !startStop; } });} Threads (Part II)
Example 2: A Simple Use of sleep(): A Timer (Cont’d) public void run(){ while (true){ try{ Thread.sleep(100); }catch(Exception e){} if (startStop) jtf.setText(Integer.toString(counter++)); } } public static void main(String args[]){ new Thread(new TimerThread()).start(); } } • Note that the sleep() method must be associated with a thread. • To see this, modify the run() method into myRun() and change the body of actionPerformed() to consist only of the call to myRun(). • You’ll notice that myRun() does not allow multithreading despite the sleep() method inside it. • That is, while the method is sleeping, the CPU fails to monitor other button presses. This is because actionPerfomed() never returns because myRun() is infinite. • Associating the sleep() method with a Thread object ensures the desired behavior; allowing the CPU slice its time and services different events. Threads (Part II)
Why Use the synchronized Keyword? • In Java, when the synchronized keyword is used in a method, the JVM guarantees that only one thread will be able to run through that method at any given time, ie, there is no concurrent thread access to that block of code. A shared resource may be corrupted if it is accessed simultaneously by multiple threads. For example, two unsynchronized threads accessing the same bank account causes conflict. • These blocks of code are also known as critical sections. • You can synchronize entire methods by using the synchronized keyword in the method declaration, or you can just mark certain lines of code as synchronized. • In our next example, we demonstrate the use the synchronize keyword to make an entire method mutually exclusive, or thread-safe: • public synchronized void myMethod() {..} • Note that code that is thread-safe (i.e, protected by synchronized) is slower than code that is not. • For better performance, you should only make those lines of your code thread-safe, that absolutely need to be. Threads (Part II)
Example 3: Demonstrating the Effect of synchronized int get(){ return balance; } } public class UnsafeBankAccount extends Thread{ BanckAccount ba; public static void main( String[] args ){ BanckAccount ba = new BanckAccount(0); for(int i=0; i<10; i++) new UnsafeBankAccount(ba).start(); } class BanckAccount{ private int balance; public BanckAccount(int balance){ this.balance = balance; } void doNothing(){ depositWithdraw(10); depositWithdraw(20); depositWithdraw(30); } void depositWithdraw(int money){ try { balance += money; Thread.sleep((long)(Math.random()*1000)); balance -= money; } catch(Exception e){} } Threads (Part II)
Example 3: Effect of synchronized (Cont’d) public UnsafeBankAccount(BanckAccount ba){ this.ba = ba; } public void run(){ doWork(); } public void doWork(){ System.out.println(getName()+" got balance: "+ba.get()); ba.doNothing(); System.out.println(getName()+" got balance: "+ba.get()); } } Threads (Part II)
Example 3: Effect of synchronized (Cont’d) • Designing classes whose methods could be accessed by multiple threads requires special care because there is no way of knowing what thread will invoke what method (and in what sequence) on an object of this class. • For this reason when you run the above program you find that the balance printed is not zero (in some cases) as it should be. • Now, you might think that this example shouldn't even have any problems because the depositWithdraw() method in the BankAccount class doesn't do anything. • Well, depositing and withdrawing from a bank account are NOT atomic operations; [an atomic operation is one that the JVM guarantees will occur without any interruptions (for example updating a register in a processor)]. • When the many threads from the UnsafeBankAccount class are trampling through the bank account object, the bank balance does change! • This is because other threads mess with the balance before each thread gets done using it; so as one thread adds money, and is just about to remove it, another thread could go in and remove more money, and the balance changes! • There are an infinite number of possibilities (most of which are undesirable) when a lot of threads are let loose on this poor object. Threads (Part II)
Example 3: Effect of synchronized (Cont’d) • The solution to this problem would be to synchronize the method (depositWithdraw()) in the BankAccount class that accesses/modifies the bank balance. • This would guarantee that only one thread was trampling through it at any given time. • Also it is necessary to synchrnonize the accessor method too! Why? • Well, the methods to add and remove money from the account are so fast that when I use get() it sometimes returns the value of the bank balance in the middle of doNothing(). • So one thread is in the middle of executing doNothing() at which point the bank balance is non-zero. • At this same instant, another thread calls get() and gets a non-zero value of the bank account. • The way to solve this is to synchronize the get() method too, in order to ensure that the real bank balance is returned. • On the other hand, if you wish to monitor the state of this object at all times, then you might want to leave the get() unsynchronized. It all depends on what you want to do. Threads (Part II)
Output with Synchronized Methods synchronized void depositWithdraw(int money) synchronized int get(){ return balance; } Threads (Part II)