CS61B, Spring 2003 Discussion #17 Amir Kamil UC Berkeley 5/12/03

Similar documents
Multithreaded Programming

Threads Chate Patanothai

Overview. CMSC 330: Organization of Programming Languages. Concurrency. Multiprocessors. Processes vs. Threads. Computation Abstractions

Multitasking Multitasking allows several activities to occur concurrently on the computer. A distinction is usually made between: Process-based multit

Computation Abstractions. Processes vs. Threads. So, What Is a Thread? CMSC 433 Programming Language Technologies and Paradigms Spring 2007

Operating Systems CMPSCI 377 Spring Mark Corner University of Massachusetts Amherst

THREADS AND CONCURRENCY

JAVA and J2EE UNIT - 4 Multithreaded Programming And Event Handling

Threads and Parallelism in Java

Concurrency & Parallelism. Threads, Concurrency, and Parallelism. Multicore Processors 11/7/17

Java Threads. Introduction to Java Threads

Threads, Concurrency, and Parallelism

Java Threads. Written by John Bell for CS 342, Spring 2018

Part IV Other Systems: I Java Threads

CS 556 Distributed Systems

Multiple Inheritance. Computer object can be viewed as

CMSC 330: Organization of Programming Languages

Introduction to Java Threads

CSCD 330 Network Programming

Synchronization synchronization.

Java Threads. COMP 585 Noteset #2 1

Contents. 6-1 Copyright (c) N. Afshartous

CS11 Java. Fall Lecture 7

CSCD 330 Network Programming

RACE CONDITIONS AND SYNCHRONIZATION

7. MULTITHREDED PROGRAMMING

Synchronization in Java

Week 7. Concurrent Programming: Thread Synchronization. CS 180 Sunil Prabhakar Department of Computer Science Purdue University

CmpSci 187: Programming with Data Structures Spring 2015

Java Threads and intrinsic locks

The Dining Philosophers Problem CMSC 330: Organization of Programming Languages

Only one thread can own a specific monitor

CMSC 330: Organization of Programming Languages

Overview. Processes vs. Threads. Computation Abstractions. CMSC 433, Fall Michael Hicks 1

CMSC 330: Organization of Programming Languages. The Dining Philosophers Problem

THREADS & CONCURRENCY

An Introduction to Programming with Java Threads Andrew Whitaker University of Washington 9/13/2006. Thread Creation

CISC 4700 L01 Network & Client-Server Programming Spring Cowell Chapter 15: Writing Threaded Applications

CMSC 330: Organization of Programming Languages. Threads Classic Concurrency Problems

CS18000: Programming I

Unit 4. Thread class & Runnable Interface. Inter Thread Communication

Deadlock. Only one process can use the resource at a time but once it s done it can give it back for use by another process.

04-Java Multithreading

Exercise Session Week 8

Question Points Score Total 100

Animation Part 2: MoveableShape interface & Multithreading

Module - 4 Multi-Threaded Programming

Learning from Bad Examples. CSCI 5828: Foundations of Software Engineering Lecture 25 11/18/2014

Programming Language Concepts: Lecture 11

COMPSCI 230 Threading Week8. Figure 1 Thread status diagram [

Exercise Session Week 8

CMSC 132: Object-Oriented Programming II. Threads in Java

CS 2112 Lecture 20 Synchronization 5 April 2012 Lecturer: Andrew Myers

Basics of. Multithreading in Java

Problems with Concurrency. February 19, 2014

Definition: A thread is a single sequential flow of control within a program.

CS 159: Parallel Processing

Chair of Software Engineering. Java and C# in Depth. Prof. Dr. Bertrand Meyer. Exercise Session 8. Nadia Polikarpova

CMSC 433 Programming Language Technologies and Paradigms. Spring 2013

Need for synchronization: If threads comprise parts of our software systems, then they must communicate.

Lecture 9: Introduction to Monitors

Concurrent Programming using Threads

THREADS & CONCURRENCY

What is a thread anyway?

CS 351 Design of Large Programs Threads and Concurrency

CS193k, Stanford Handout #8. Threads 3

Synchronization. CS 475, Spring 2018 Concurrent & Distributed Systems

What is a Thread? Why Multicore? What is a Thread? But a fast computer runs hot THREADS AND CONCURRENCY. Concurrency (aka Multitasking)

JAVA. Lab 12 & 13: Multithreading

Multithreaded Programming

Concurrent Computing CSCI 201 Principles of Software Development

Concurrency and Java Programming

27/04/2012. We re going to build Multithreading Application. Objectives. MultiThreading. Multithreading Applications. What are Threads?

Multithreaded Programming Part II. CSE 219 Stony Brook University, Department of Computer Science

Principles of Software Construction: Concurrency, Part 2

Chapter 32 Multithreading and Parallel Programming

Synchronization in Concurrent Programming. Amit Gupta

Contribution:javaMultithreading Multithreading Prof. Dr. Ralf Lämmel Universität Koblenz-Landau Software Languages Team

Real-Time and Concurrent Programming Lecture 4 (F4): Monitors: synchronized, wait and notify

Computation Abstractions. CMSC 330: Organization of Programming Languages. So, What Is a Thread? Processes vs. Threads. A computer.

CS180 Review. Recitation Week 15

INF 212 ANALYSIS OF PROG. LANGS CONCURRENCY. Instructors: Crista Lopes Copyright Instructors.

COMP346 Winter Tutorial 4 Synchronization Semaphores

Chapter 6: Process Synchronization. Operating System Concepts 8 th Edition,

Threads & Concurrency

Concurrent Programming Lecture 10

Threads & Concurrency

Advanced Concepts of Programming

Deadlock and Monitors. CS439: Principles of Computer Systems February 7, 2018

Unit - IV Multi-Threading

Java s Implementation of Concurrency, and how to use it in our applications.

The New Java Technology Memory Model

G51PGP Programming Paradigms. Lecture 009 Concurrency, exceptions

Multiple Choice Questions: Identify the choice that best completes the statement or answers the question. (15 marks)

Java Programming Lecture 23

Reintroduction to Concurrency

CMSC 433 Programming Language Technologies and Paradigms. Concurrency

UNIT V CONCURRENT PROGRAMMING

CSE332: Data Abstractions Lecture 22: Shared-Memory Concurrency and Mutual Exclusion. Tyler Robison Summer 2010

Exception Handling. Sometimes when the computer tries to execute a statement something goes wrong:

Transcription:

CS61B, Spring 2003 Discussion #17 Amir Kamil UC Berkeley 5/12/03 Topics: Threading, Synchronization 1 Threading Suppose we want to create an automated program that hacks into a server. Many encryption schemes use a very large product of two primes as a key. The product is publicized and can be used to encrypt data, but you can only decrypt data if you know what the two primes are. So let s have our program try factoring the key in order to break the encryption. Unfortunately, factoring is hard, exponentially so (O(2 n ) in the number of bits in the key; keys of hundreds or thousands of bits are typical). So the our program will sit there forever trying to break the key, while we want it to try other hacking techniques as well. We can use threading in order to allow it to do both at the same time. A thread is a stream of execution within a program. A program can consist of multiple threads that appear to run simultaneously, so it is possible for a program to do multiple things at once. For example, if you are writing a chess program, you can have the computer player think while it is the human player s turn. This act of thinking would be done in a different thread than the main program so that the human player can input his move. In Java, a thread is represented by a Thread object. Associated with a particular thread is a method that runs when the thread is started. This method is analagous to the main() method that gets executed when a program is started and must have the signature public void run(). In order to have a piece of code that runs in its own thread, a class that either extends Thread or implements Runnable must be defined. The piece of code then must be placed in the run() method of that class. In the case of a class that extends Thread, the thread is started by calling its start() method (inherited from the Thread class). In the case of a Runnable, the class must be wrapped by a generic Thread object, and the start() method of that Thread object called. The start() method does the actual thread creation and calls the appropriate run() method. We can now modify our program to run the factoring in a separate thread and try other hacking methods at the same time. 2 Synchronization While we now have programs that can execute multiple pieces of code at once, we have now introduced synchronization issues that must be dealt with. We may have certain pieces of code that we only want one thread to execute at a time, or resources that we don t multiple threads to simultaneously access. A simple example is a file that we are reading and writing. If two threads simultaneously write a file, then the written data can be garbled in unexpected ways. Think of what happens if two threads print something to the screen at the same time. The result will be unintelligible. 2.1 The synchronized Keyword In the case of code fragments that we want to restrict to a single thread, the Java keyword synchronized is sufficient. A block of code that is synchronized can only be executed by one thread at a time. Any block can be synchronized, including entire methods, in which case the method can be declared synchronized. We will not cover synchronized blocks. 1

class Hacker { int factorkey(int key) { for (int i = 2; i < key; i++) { if (num % i == 0) { return i; return 0; Figure 1: A program that factors numbers. In the example in figure 3, only one thread at a time can be in either the writefile() method or the synchronized block in the readfile() method. The exact rules for synchronized are a bit complicated, and are as follows: 1. if two static methods of a class are synchronized, then only one thread can be in either of them at once 2. if two instance methods of a class are synchronized, then only one thread can be in either of them at once for a particular instance of the class 3. if an instance method of a class is synchronized, then a thread can be in the method for one instance at the same time another thread is in the method for another instance 4. if an instance and a static method of a class are both synchronized, then one thread can be in one and another in the other at the same time To illustrate these rules, consider the program in figures 4 and 5. It creates four threads, with two calling a synchronized instance method (Synch.foo()) on the same instance, the third calling the same method on another instance, and the last calling a static synchronized method (Synch.bar()) in the same class. The methods just wait forever, so a thread that enters one of the methods claims the synchronization forever. What does the program print? The result is Thread-0 executing foo() on object #0 Thread-3 executing bar() Thread-2 executing foo() on object #1 Press enter to exit... Exiting... modulo the order of the first four statements (it varies in each run, due to the nondeterministic order in which the threads get run). So you see that thread 1 doesn t execute foo() since it tries calling it on the same instance as thread 0, which has claim to the instance s synchronization. But thread 2 executes foo() at the same time on a different instance. And thread 3 executes the static method bar() at the same time. But thread 4 cannot execute the instance method baz() at the same time thread 0 executes foo() on the same instance. As you can see, the effect of the synchronized keyword is complicated. Without a thorough knowledge of how it works, it is very easy to make mistakes. Perhaps we can use higher level constructs to abstract away the details of how the keyword works. 2.2 Locks There are many different synchronization constructs, and perhaps the simplest is a lock. A lock can be acquired and released, but as long as one thread owns a lock, any other thread that attempts to acquire it must wait until the first one releases it. 2

class Factor extends Thread { int number, factor; boolean running = true; Factor (int number) { this.number = number; public void run() { factor = findfactor(number); running = false; int findfactor(int num) { for (int i = 2; i < num; i++) { if (num % i == 0) { return i; return 0; class Hacker { Factor f; void hack(server s) { f = new Factor(s.getKey()); f.start(); // creates a new thread, calls f.run() while (f.running) { trysomethingelse(s); // now we know the key, so we can crash the server crashserver(s, f.factor);d void trysomethingelse(server s) { sendvirus(s);...... Figure 2: A modified program that both factors and tries other hacking methods at the same time. public synchronized void writefile(string s) {... public synchronized String readfile() {... Figure 3: Examples of synchronized code. 3

class Synch { static int nextid = 0; int id = nextid++; // the unique ID of this instance static Synch shared = new Synch(); // a shared instance of this class static boolean RUN = true; * this instance */ synchronized void foo() { " executing foo() on object #" + id); catch (Exception e) { * this class */ synchronized static void bar() { " executing bar()"); catch (Exception e) { * this instance */ synchronized void baz() { " executing baz() on object #" + id); catch (Exception e) { Figure 4: A class to test the effects of the synchronized keyword 4

class SynchTest { /** Tests the effects of the synchronized keyword */ public static void main(string[] args) throws java.io.ioexception { class A extends Thread { /** Runs a synchronized method on the shared instance */ public void run() { shared.foo(); ; class B extends Thread { /** Runs a synchronized method on a different instance */ public void run() { new Synch().foo(); ; class C extends Thread { /** Runs a synchronized class method */ public void run() { Synch.bar(); ; class D extends Thread { /** Runs a different synchronized method on the shared instance */ public void run() { shared.baz(); ; Thread t0 = new A(); Thread t1 = new A(); Thread t2 = new B(); Thread t3 = new C(); Thread t4 = new D(); t0.start(); t1.start(); t2.start(); t3.start(); t4.start(); // Wait for the user to be satisfied that nothing more is going to happen. System.out.println("Press enter to exit..."); System.in.read(); System.out.println("Exiting..."); System.exit(0); Figure 5: A program to test the effects of the synchronized keyword. 5

private Lock filelock = new Lock(); public void writefile(string s) { filelock.acquire();... filelock.release(); public String readfile() { filelock.acquire();... filelock.release(); Figure 6: Using a lock to provide synchronized access to a file. class Lock { private boolean locked; public synchronized void acquire() { while (locked) { locked = true; public void release() { locked = false; Figure 7: A lock class that can be used to restrict access to resources. Using a lock, we can rewrite the writefile() and readfile() methods to use locks instead of the synchronized. This can be done be using a lock that must be acquired before the file can be accessed. Only one thread can hold a lock, and all other threads must wait until the owner releases the lock. The rewritten code is in figure 6. Unfortunately, the Java API does not have a lock implementation, so we ll have to implement our own. Using synchronized blocks, it isn t too difficult. The acquire() method must be synchronized so only one thread can acquire a lock at a time. We also need a flag in order to determine whether or not the lock has been acquired. A lock implementation is shown in figure 7. Unfortunately, our lock code in figure 7 is inefficient. The loop in the acquire() method is an example of busy waiting, where a thread executes useless instructions in order to pass time. (Even if we add a call to Thread.sleep() as in figure 4, if we don t sleep long enough, it will still be inefficient, but if we sleep too long, then the thread won t acquire the lock immediately after it is released.) Instead, Java provides methods to allow threads to wait without using CPU time. Calling the wait() method of an object puts a thread to sleep on that object until another thread calls notify() or notifyall() on that object (notify() wakes one thread while notifyall() wakes all). An object relinquishes all claims on synchronized blocks when it sleeps and must reacquire them before continuing. We can rewrite Lock to be more efficient using these methods. In addition, our lock code in figure 7 has protection issues. Any thread can call the release() method, even one that doesn t own the lock. Using the Thread.currentThread() method, we can prevent this from happening. The revised lock code is in figure 8. 2.3 Equivalence of synchronized and Locks (Optional) An interesting question is whether or not locks are equivalent to the synchronized keyword. By equivalent, we mean it is possible to do everything you can with locks that you can with synchronized, and vice versa. Since we implemented locks using synchronized, we have proven that it is possible to do everything with 6

class Lock { private boolean locked; Thread owner; public synchronized void acquire() { while (locked) { wait(); catch (InterruptedException e) { locked = true; owner = Thread.currentThread(); public synchronized void release() { if (owner == Thread.currentThread()) { locked = false; owner = null; notifyall(); Figure 8: A more efficient lock. acquire() must catch InterruptedException since wait() can throw it. release() must be synchronized to allow notifyall() to be called. Note that threads can attempt to acquire the lock while others are sleeping since they give up their synchronization claims. synchronized that you can do with locks. All that is left is to prove the reverse. This is more than just a theoretical exercise. If we can show that locks and synchronized are equivalent, perhaps we can better understand how synchronized works throught the equivalence relation. If the relation is simple enough, we won t have to remember all the complicated rules that define how synchronized works. In order to prove the equivalence, we will try to show how an arbitrary use of synchronized can be replaced by use of a lock. (Actually, since we are only discussing synchronized methods, we will only go as far as showing how such methods can have the same behavior using locks. It is not much harder to show how synchronized blocks can be replaced with locks as well.) To start with, recall how we reimplemented our file access methods using a lock. To do so, we had to acquire a lock at the beginning of each method, and release it at the end. Let s try generalizing this to arbitrary synchronized methods. The main question then, is what lock should we use in each method? Let s try to answer this question by analyzing each rule above for how synchronized works: 1. In order to prevent multiple threads from executing a class s static methods at once, all static methods of a particular class should use the same lock. But static methods of different classes should use different locks. 2. In order to prevent multiple threads from executing instance methods concurrently, all instance methods of a particular instance should use the same lock. 3. In order to allow different threads to execute the same instance method on different instances at the same time, instance methods of different instances should use different locks. 4. In order to allow different threads to execute a static and an instance method at the same time, static and instance methods should use different locks. (This also follows from the rule above.) The simplest scheme to accomplish these goals is to associate a lock with each class, and a lock with each instance. Then the formerly synchronized static methods would use the class s lock, and the instance 7

class Synch { static int nextid = 0; int id = nextid++; // the unique ID of this instance static Synch shared = new Synch(); // a shared instance of this class static boolean RUN = true; private static Lock ourlock = new Lock(); // lock for class methods private Lock mylock = new Lock(); // lock for instance methods * this instance */ void foo() { mylock.acquire(); " executing foo() on object #" + id); catch (Exception e) { mylock.release(); * this class */ static void bar() { ourlock.acquire(); " executing bar()"); catch (Exception e) { ourlock.release(); * this instance */ void baz() { mylock.acquire(); " executing baz() on object #" + id); catch (Exception e) { mylock.release(); Figure 9: The Synch class implemented with locks instead of the synchronized keyword. 8

class Lock { private int locked = 0; Thread owner; public synchronized void acquire() { if (Thread.currentThread() == owner) { locked++; return; while (locked > 0) { wait(); catch (InterruptedException e) { locked = 1; owner = Thread.currentThread(); public synchronized void release() { if (owner == Thread.currentThread()) { locked--; if (locked == 0) { owner = null; notifyall(); Figure 10: A lock that allows the owner to call acquire() multiple times. methods would use the instance s lock. Figure 9 illustrates this translation for the Synch class. The result of running SynchTest is exactly the same as before. There is a small problem with our translation. What happens if methods A() and B() are both synchronized, and A() calls B()? A thread will acquire the lock at the beginning of A(), and then again at the beginning of B(). But the lock is taken (by that thread), so the thread will go to sleep. Since the owner is asleep waiting for the owner to wake it up, it will sleep forever. We can solve this problem by checking if the thread that calls acquire() is the owner, and not going to sleep if it is. But then what happens when B() returns? The thread will release the lock, even though A() has not finished executing. This violates the synchronization requirement. A fix will require keeping track of how many times the owner of a lock has called acquire(), and forcing it to release the lock the same number of times. (The term for this is a semaphore, but we won t make that distinction here.) The resulting lock implementation is in figure 10. We have now shown that locks and the synchronized keyword are equivalent. This means that we can accomplish any synchronization allowed by Java using only locks. Also, since the equivalence relation is so simple, we don t need to know the rules of how synchronized works. Instead, we can just remember that the equivalent is that synchronized static methods use a class lock, and that synchronized instance methods use an instance lock. 2.4 Conclusion The introduction of parallelism into programs can potentially result in faster programs. This, however, comes at a price. Parallel programs can interfere with each other, since sharing resources can be a problem. With the use of proper synchronization, this issue can be resolved, though an extra level of complexity is 9

added to the program. Synchronization issues are particularly important in operating systems. While it may be unlikely that two threads execute the same code at the exact same time, we don t want our computers to crash when it does happen. It s errors like these that are the most difficult to fix, since they are difficult to reproduce. If you are running a Windows machine and want to see an example of synchronization, try opening a file in Microsoft Word and then in WordPad at the same time. You ll see that the operating system won t let you. 10