Threads and Locks, Part 2. CSCI 5828: Foundations of Software Engineering Lecture 08 09/18/2014

Similar documents
Recap. Contents. Reenterancy of synchronized. Explicit Locks: ReentrantLock. Reenterancy of synchronise (ctd) Advanced Thread programming.

Threads and Locks. CSCI 5828: Foundations of Software Engineering Lecture 09 09/22/2015

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

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

COMP 322: Fundamentals of Parallel Programming

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

CMSC 330: Organization of Programming Languages

Introduction to Concurrent Software Systems. CSCI 5828: Foundations of Software Engineering Lecture 08 09/17/2015

The Dining Philosophers Problem CMSC 330: Organization of Programming Languages

COMP 322: Fundamentals of Parallel Programming. Lecture 30: Java Synchronizers, Dining Philosophers Problem

Introduction to Concurrent Software Systems. CSCI 5828: Foundations of Software Engineering Lecture 12 09/29/2016

G52CON: Concepts of Concurrency

Chair of Software Engineering. Java and C# in depth. Carlo A. Furia, Marco Piccioni, Bertrand Meyer. Java: concurrency

Summary Semaphores. Passing the Baton any await statement. Synchronisation code not linked to the data

JFokus Finding And Solving Java Deadlocks

More thread safety and Concurrent collections. Nasser Giacaman CompSci 230: Software Design and Construction

CMSC 330: Organization of Programming Languages

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

Synchronized Methods of Old Versions of Java

Clojure Concurrency Constructs. CSCI 5828: Foundations of Software Engineering Lecture 12 10/02/2014

More Synchronization; Concurrency in Java. CS 475, Spring 2018 Concurrent & Distributed Systems

MultiThreading 07/01/2013. Session objectives. Introduction. Introduction. Advanced Java Programming Course

Advanced Java Programming Course. MultiThreading. By Võ Văn Hải Faculty of Information Technologies Industrial University of Ho Chi Minh City

Grand Central Dispatch and NSOperation. CSCI 5828: Foundations of Software Engineering Lecture 28 12/03/2015

CMSC 433 Programming Language Technologies and Paradigms Fall Locks & Conditions

Chapter 32 Multithreading and Parallel Programming

CONCURRENT API AND PARALLEL PROGRAMMING

Concurrency, Thread. Dongkun Shin, SKKU

Lecture 27: Safety and Liveness Properties, Java Synchronizers, Dining Philosophers Problem

Threads and Java Memory Model

G Programming Languages Spring 2010 Lecture 13. Robert Grimm, New York University

Only one thread can own a specific monitor

Principles of Software Construction: Objects, Design and Concurrency. The Perils of Concurrency, Part 2 Can't live with it. Cant live without it.

Principles of Software Construction: Objects, Design and Concurrency. The Perils of Concurrency, Part 2 (Can't live with it, can't live without it.

The New Java Technology Memory Model

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

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

Favoring Isolated Mutability The Actor Model of Concurrency. CSCI 5828: Foundations of Software Engineering Lecture 24 04/11/2012

Dealing with Issues for Interprocess Communication

CS 455: INTRODUCTION TO DISTRIBUTED SYSTEMS [THREADS] Frequently asked questions from the previous class survey

Threads Questions Important Questions

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

Synchronization Lecture 24 Fall 2018

Buffer & File Optimization. Cloud Databases DataLab CS, NTHU

Thread Safety. Review. Today o Confinement o Threadsafe datatypes Required reading. Concurrency Wrapper Collections

Deadlock and Monitors. CS439: Principles of Computer Systems September 24, 2018

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

CS11 Java. Fall Lecture 7

re-exam Concurrent Programming tda383/dit390 Date: Time: 14:00 18:00 Place: Maskinhuset (M)

Synchronization. CS 475, Spring 2018 Concurrent & Distributed Systems

CMSC 433 Programming Language Technologies and Paradigms. Spring 2013

COMP31212: Concurrency A Review of Java Concurrency. Giles Reger

Synchronization COMPSCI 386

G52CON: Concepts of Concurrency

Final Score Name SOLUTION ID Extra Credit. 15% of course grade

Operating Systems CMPSCI 377 Spring Mark Corner University of Massachusetts Amherst

Java Concurrency. Towards a better life By - -

Concurrent Queues, Monitors, and the ABA problem. Companion slides for The Art of Multiprocessor Programming by Maurice Herlihy & Nir Shavit

CSE 332: Data Structures & Parallelism Lecture 17: Shared-Memory Concurrency & Mutual Exclusion. Ruth Anderson Winter 2019

Daemon thread, 7 Deadlock, 27

Module 6: Process Synchronization. Operating System Concepts with Java 8 th Edition

-Aditya Bhave CSCI-5448 Graduate Presentation

Homework #3. CS 318/418/618, Fall Handout: 10/09/2017

EI 338: Computer Systems Engineering (Operating Systems & Computer Architecture)

Java Platform Concurrency Gotchas

THREADS & CONCURRENCY

Operating Systems. Designed and Presented by Dr. Ayman Elshenawy Elsefy

CSE 332: Locks and Deadlocks. Richard Anderson, Steve Seitz Winter 2014

Operating Systems. Lecture 4 - Concurrency and Synchronization. Master of Computer Science PUF - Hồ Chí Minh 2016/2017

Process Management And Synchronization

Concurrent Programming Benoît Garbinato

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

Parallel Computing. Prof. Marco Bertini

COMP 213. Advanced Object-oriented Programming. Lecture 23. Shared Variables and Synchronization

Threads and Synchronization. Kevin Webb Swarthmore College February 15, 2018

CS 162 Operating Systems and Systems Programming Professor: Anthony D. Joseph Spring 2004

CSL373: Lecture 5 Deadlocks (no process runnable) + Scheduling (> 1 process runnable)

Executive Summary. It is important for a Java Programmer to understand the power and limitations of concurrent programming in Java using threads.

Java ReentrantLock (Part 1)

Synchronization. Announcements. Concurrent Programs. Race Conditions. Race Conditions 11/9/17. Purpose of this lecture. A8 released today, Due: 11/21

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

Final Score ID Extra Credit. 15% of course grade

Design Approaches for Concurrent Systems. CSCI 5828: Foundations of Software Engineering Lecture 08 02/09/2012

Dr.-Ing. Michael Eichberg

Dr.-Ing. Michael Eichberg

Midterm on next week Tuesday May 4. CS 361 Concurrent programming Drexel University Fall 2004 Lecture 9

Java Concurrency For Humans. Cay Horstmann Author of Core Java (10 editions since 1996)

Race Conditions & Synchronization

SSC - Concurrency and Multi-threading Java multithreading programming - Synchronisation (II)

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

CSCI 201L Final Written Spring % of course grade

Models of concurrency & synchronization algorithms

Today: Synchronization. Recap: Synchronization

EECS 482 Introduction to Operating Systems

Synchronization Lecture 23 Fall 2017

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

RACE CONDITIONS AND SYNCHRONIZATION

Virtual Machine Design

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

Lecture 10: Multi-Object Synchronization

Transcription:

Threads and Locks, Part 2 CSCI 5828: Foundations of Software Engineering Lecture 08 09/18/2014 1

Goals Cover the material presented in Chapter 2 of our concurrency textbook In particular, selected material from Days 2 and 3 java.util.concurrent ReentrantLock AtomicVariables Thread Pools Producer Consumer Example Warning: the book expects you to download a 10GB file that expands to be 46 GB in order to run these examples! I ve done this for you. (You re welcome. ) 2

What s the easy way? Last lecture, we looked at doing concurrency the hard way So, what s the easy way? We are not necessarily going to answer that question in this lecture But, we are going to look at the java.util.concurrency library to help make it less painful to create concurrent software <http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ package-summary.html> As we move forward, we ll look at other models and techniques that all attempt to reduce the complexity associated with concurrent programming However, each new model/technique will introduce its own set of difficulties such as having to learn Clojure. 3

Synchronized Keyword => Intrinsic Locks We ve been using the synchronized keyword to try to address the problems we ve been seeing in our concurrent programs so far The problem with this keyword is that it makes use of an inflexible lock that our book calls intrinsic locks It is inflexible because A thread blocked on an intrinsic lock cannot be interrupted It s all or nothing A thread blocked on an intrinsic lock cannot have a time out There s only one type of intrinsic lock (reentrant) and only one way to acquire it (with the synchronized keyword) 4

java.util.concurrent.locks.reentrantlock The ReentrantLock class from java.util.concurrent.locks allows us to gain the benefit of fine-grained locking without having to use the synchronized keyword To use them, you first instantiate a lock (and stash it somewhere) Lock lock = new ReentrantLock(); Then you follow this pattern lock.lock(); try { «use shared resource» } finally { lock.unlock(); } 5

First benefit: Abstraction (I) Note this line of code carefully Lock lock = new ReentrantLock(); The concrete class is ReentrantLock. Our variable is of type Lock This gives us a nice abstraction with the following methods lock() acquire the lock and block if needed lockinterruptibly() acquire the lock, block if needed, allow interruption newcondition() bind a Condition to this lock trylock() and trylock( ) acquire the lock without blocking or with a time out unlock() release the lock 6

First benefit: Abstraction Currently java.util.concurrent.locks provide us with two types of locks ReentrantLock and ReentrantReadWriteLock But the interface Lock allows us to write code that is not dependent on the concrete classes and could change if better options are added in the future This abstraction also addresses the problem of having only one way to acquire an intrinsic lock The Lock interface gives us four different ways of acquiring a lock blocking, non-blocking, with a time out, with the option of being interrupted This flexibility allows us to design concurrent systems with more powerful constructs and avoid the problems we saw in Lecture 7 7

Second Benefit: Thread Interruption The book has two programs to demonstrate the ability (or lack thereof) to interrupt threads that are blocked on locks With an intrinsic lock, there s no way to do it if a thread becomes blocked and the semantics of your program will never allow it to become unblocked then it s stuck until you kill the JVM With the Lock interface, you simply acquire it with the lockinterruptibly() method and then Thread.interrupt() behaves as you would expect DEMO 8

Third Benefit: Timeouts (I) With the Lock interface, you have the option of calling trylock() This is a nonblocking call that returns TRUE if the lock is acquired if (lock.trylock()) { Lock acquired } else { Lock not acquired; act accordingly } You also have the option to specify a timeout (from nanoseconds to days!) if (lock.trylock(50l, TimeUnit.SECONDS)) { Lock acquired } else { Timeout occurred; act accordingly } 9

Third Benefit: Timeouts (II) The book provides an example of the Dining Philosophers program that makes use of timeouts We don t have to worry about acquiring chopsticks in the same order We instead adopt the following strategy acquire a lock on the left chopstick acquire a lock on the right chopstick using trylock() with a timeout If that fails, release the lock on the left chopstick and try again This works BUT you can have a problem with LIVELOCK Live lock is the situation in which a program does not make progress because the threads lock, time out, and then immediately lock again You spend your time dealing with locks and not performing work 10

Note: Skipping Hand-over-Hand Locking The book has an example of using ReentrantLocks to allow multiple threads to access a linked list at the same time including allowing multiple insertions to occur without damaging the overall structure of the list and allowing iteration and other operations to occur in parallel on the list I m going to skip this example as it is fairly low-level The key idea is how the locks are acquired as a thread moves through the list You acquire a lock on node i and then try to acquire a lock on node i+1 Once you have the lock on node i+1, you can release the lock on node i 11

Fourth Benefit: Condition Variables (I) Condition Variables are used when your thread s logic involves waiting for an event to occur (or a condition to become true) before it does its work If the event has not happened or the condition is false, we want the thread to block and not consume the scheduler s time Coding this logic can be quite challenging You may have a lot of threads waiting for the condition to become true As a result, you need a queue or a collection class to hold the threads while they are waiting When the condition becomes true, you need a way to wake up the threads that are waiting, and let them try to do their work Condition variables make coding this type of logic straightforward 12

Fourth Benefit: Condition Variables (II) To work with a conditional variable, you follow this pattern ReentrantLock lock = new ReentrantLock(); Condition condition = lock.newcondition(); lock.lock() try { while (!«condition is true») { condition.await(); } «use shared resources» } finally { lock.unlock(); } The key to this pattern is that the variable and the condition are tied together The thread has to acquire the lock and then check the condition If the condition is false, it needs to wait. The call to await() blocks the thread and unlocks the lock atomically, allowing other threads to access the shared resources 13

Fourth Benefit: Condition Variables (III) To work with a conditional variable, you follow this pattern ReentrantLock lock = new ReentrantLock(); Condition condition = lock.newcondition(); lock.lock() try { while (!«condition is true») { condition.await(); } «use shared resources» } finally { lock.unlock(); } The key to this pattern is that the variable and the condition are tied together If some other thread performs work that may have changed the condition, it calls signal() or signalall() on the condition. The former wakes up a single thread that was blocked on the condition; the latter wakes them all up If signalall() is used, all of the threads battle to reacquire the lock 14

Fourth Benefit: Condition Variables (IV) To work with a conditional variable, you follow this pattern ReentrantLock lock = new ReentrantLock(); Condition condition = lock.newcondition(); lock.lock() try { while (!«condition is true») { condition.await(); } «use shared resources» } finally { lock.unlock(); } The key to this pattern is that the variable and the condition are tied together It is thus guaranteed that when await() returns, the thread once again has acquired the lock. It then checks the condition to see if it is indeed true. If so, it performs its work and releases the lock. If not, it blocks once again with a call to await() 15

Fourth Benefit: Condition Variables (V) The book provides an example of using a Condition Variable to provide a solution to the dining philosopher that a) avoids deadlock b) enables significantly more concurrency than previous solutions With the previous solutions, it was highly likely that only one philosopher was able to eat at a time All the other philosophers have one chopstick and are waiting for the other one to become available With this solution, at least two philosophers can be eating at one time. That number goes up, if we increase the number of philosophers that are sitting around the table 16

java.util.concurrent.atomic The java.util.concurrent.atomic package contains classes that support lockfree thread-safe programming on single variables They provide an easy way to implement single variables that need to be shared between multiple threads and you want to avoid the use of locks to enable maximum concurrency A common use case includes variables that need to be incremented or decremented as events occur without losing any of those operations due to race conditions Another use case is the ability to share object references across threads, such that a change in the reference is seen by all threads No memory barrier issues, the change is guaranteed to be visible in all threads The book updates the counter example using an AtomicInteger. DEMO 17

Thread Pools It takes time to create threads For servers, you want to avoid having to create threads on demand It will slow you down, if you have to wait for a thread to spin up each time you receive a connection Code that creates threads on demand, typically do not have logic to prevent creating one thread per request If that s true and you get 1000s of requests, your code will blindly create 1000s of threads, which will bring your machine to its knees A much better strategy is to use a thread pool You create all of the threads you are going to use up front When you need a request to be handled, you wrap it up in a Runnable and hand it to the pool If a thread is available, it executes immediately; otherwise it waits 18

Using Thread Pools The code to create a thread pool is straightforward public class Task implements Runnable int threadpoolsize = Runtime.getRuntime().availableProcessors() * 2; ExecutorService executor = Executors.newFixedThreadPool(threadPoolSize); executor.execute(new Task( )); You figure out how large you want your thread pool to be You then create a new thread pool (called an ExecutorService in Java) and pass in that size; when you need something done, call execute() How many threads should you allocate Are your tasks computationally intensive? => one thread per core Are your tasks mainly performing I/O? => lots of threads! 19

Producer-Consumer Example (I) The book makes use of a large data file (46 GB!) to provide a real world example of an application that can see performance gains with concurrency I discovered in my testing that there is a problem with page 39904 that causes java.text.rulebasedbreakiterator to generate an ArrayIndexOutOfBoundsException Rather than try to debug that problem, I restricted my experiments with this file to the first 39,000 pages. I also discovered that Java would sometimes run out of memory when processing the XML file. To give the JVM more memory, I would execute the program with this command env MAVEN_OPTS="-Xms2048m -Xmx4096m" mvn -q -e exec:java This gives the program 2 to 4 GB of memory to use if needed 20

Producer-Consumer Example (II) The first program, WordCount, is a single threaded version of the program It shows how long it takes to parse the first 39K pages of the XML file On my machine, it averaged 62 seconds across 5 runs Nothing special about this program The heavy lifting occurs in the Pages.java class, which does the work of instantiating an XML parser and calling it repeatedly for events that it recognizes Basically, it looks for <page> tags in the XML file and extracts information about the title and text of the page It ignores all other elements in the XML file When it has a complete page, it stops and returns it to the main() routine, which then counts the words on that page 21

Producer-Consumer Example (III) The first attempt to make this program concurrent adopts a typical producer/ consumer architecture The parser is in one thread, sticking pages into a bounded queue 100 pages max The consumer is in another thread pulling pages from the queue and counting them A poison pill page is inserted into the queue when the parser is done The consumer processes pages until it finds the poison pill and then it terminates The program now averages 52 seconds per run; this is an improvement but not by much. Activity Monitor doesn t report much in the way of concurrency, the CPU utilization is only 110%; the queue provides some separation but not by much 22

What s taking so long? What about queue size? Let s try increasing the queue s size from 100 to 1000? Maybe the producer got blocked on a full queue? No impact, processing time is still 52 seconds (on average) Let s determine if the producer is the problem Let s modify the consumer to pull pages off the queue without counting them It takes about 6 seconds to parse 39 thousands pages So, in order to speed this program up, we need to see if we can make the counting side (i.e. consumers) of the equation more concurrent 23

The new approach Queue Counter XML Parser Counter Counts Counter 24

Producer-Consumer Example (IV) We need to create multiple consumers and have them work together to pull pages off the queue and count the words in parallel The problem here is that we will have a shared resource between the consumers => the hash table that stores the counts for each word We will protect the hash table with a static lock shared by all consumers How did we do? Two threads => 109 seconds; 160% CPU utilization Three threads => 133 seconds; 167% CPU utilization There s too much contention for the lock that guards the hash table, we re seeing a slow down not a speed up! 25

Producer-Consumer Example (V) Since synchronization is slowing us down, perhaps we can avoid explicit locking and use a hash table designed for concurrent access java.util.concurrent.concurrenthashmap may do the trick! This data structure tries to allow parallel reads and writes on the hash table without explicit locking To do this correctly, however, in the presence of multiple threads, we have to adopt a different style of use When we have an update, we loop until one of these conditions is true The map has not seen this word before and we set the count to 1 The map has seen this word and we successfully increment by 1 To do this, we use putifabsent() and replace() rather than put() 26

Producer-Consumer Example (VI) The insertion code into the ConcurrentHashMap looks like this private void countword(string word) { while (true) { Integer currentcount = counts.get(word); if (currentcount == null) { if (counts.putifabsent(word, 1) == null) { break; } } else if (counts.replace(word, currentcount, currentcount + 1)) { break; } } } The loop is there since at any point some other thread may update counts out from underneath us! e.g. currentcount == null, but putifabsent()!= null 27

Producer-Consumer Example (VII) The results? 2 consumers: 50.5 seconds, 200% CPU utilization 3 consumers: 40.5 seconds, 280% CPU utilization 4 consumers: 40 seconds, 370% CPU utilization 8 consumers: 47.2 seconds (!), 560% CPU utilization We can get some speed up, but we still run into contention issues We re seeing the LIVELOCK in action, the threads are active (leading to higher CPU utilization) but they keep failing to update the hash table and so they are spinning around the loop, not making progress To get faster speedups, we need to reduce the contention close to zero 28

Producer-Consumer Example (VIII) The final solution is to have each consumer thread maintain its own counts separate from all other threads We can use a plain old HashTable and no need for locks since there will be no contention with this data structure; one HashTable per Consumer When the consumer is done counting words for its pages, it then merges its counts into a shared ConcurrentHashMap in the same way as the previous solution There might be some contention near the end of the run as each Consumer performs its merge, but for most of the run, there will be NO contention while counting 29

Producer-Consumer Example (IX) The results? 2 consumers: 32.6 seconds, 200% CPU utilization 3 consumers: 24.8 seconds, 275% CPU utilization 4 consumers: 22.7 seconds, 330% CPU utilization 8 consumers: 21.1 seconds, 480% CPU utilization We don t see a strict linear speed-up with increased cores BUT this solution is ~3x faster than the single threaded version this solution is ~2x faster than the previous version Not bad! There s still some contention at the end, but significantly reduced 30

There s more! We ve covered most of the major classes of functionality hiding in java.util.concurrent BUT there is still more to see Futures CountdownLatch and CyclicBarrier ForkJoinPool (work stealing) We ll return to java.util.concurrent later in the semester For now, we ll forge on and look at the functional approach to concurrency, the actor model, and more 31

Summary We reviewed a wide range of functionality provided by the java.util.concurrent library ReentrantLock AtomicVariables Thread Pools Producer Consumer Example These constructs help to reduce the sting of programming with Threads and Locks and make it easier to write and reason about concurrent programs 32

Coming Up Next Lecture 9: User Stories, Part 3 Lecture 10: User Stories, Part 4 33