Docsity
Docsity

Prepare for your exams
Prepare for your exams

Study with the several resources on Docsity


Earn points to download
Earn points to download

Earn points by helping other students or get them with a premium plan


Guidelines and tips
Guidelines and tips

Race Conditions - Object-Oriented Programming and Data Structures - Lecture Sl, Lecture notes of Object Oriented Programming

Major points from this lecture are: Race Conditions, Synchronization, Java Synchronization, Locking Works, Locks Are Associated With Objects, Visualizing Deadlock, Deadlocks Always Involve Cycles, Dealing With Deadlocks, Producer, Consumer . Object-Oriented Programming and Data Structures course includes program structure and organization, object oriented programming, graphical user interfaces, algorithm analysis, recursion, data structures (lists, trees, stacks, queues, heaps, search trees, has

Typology: Lecture notes

2012/2013

Uploaded on 08/20/2013

yumni
yumni 🇮🇳

5

(2)

27 documents

1 / 32

Toggle sidebar

Related documents


Partial preview of the text

Download Race Conditions - Object-Oriented Programming and Data Structures - Lecture Sl and more Lecture notes Object Oriented Programming in PDF only on Docsity! Race Conditions and Synchronization docsity.com Reminder  • A “race condition” arises if two threads try and  share some data  • One updates it and the other reads it, or both  update the data  • In such cases it is possible that we could see the  data “in the middle” of being updated  – A “race condition”: correctness depends on the update  racing to completion without the reader managing to  glimpse the in‐progress update  – Synchronization (aka mutual exclusion) solves this  docsity.com How locking works  • Only one thread can “hold” a lock at a time  – If several request the same lock, Java somehow  decides which will get it  • The lock is released when the thread leaves  the synchronization block  – synchronized(someObject) { protected code }  – The protected code has a mutual exclusion  guarantee: At most one thread can be in it  • When released, some other thread can  acquire the lock  docsity.com Locks are associated with objects  • Every Object has its own built‐in lock  – Just the same, some applications prefer to create  special classes of objects to use just for locking  – This is a stylistic decision and you should agree on  it with your teammates or learn the company  policy if you work at a company  • Code is “thread safe” if it can handle multiple  threads using it… otherwise it is “unsafe”  docsity.com Visualizing deadlock  Process A Process B X Y A has a lock on X wants a lock on Y B has a lock on Y wants a lock on X docsity.com Higher level abstractions * Locking is a very low-level way to deal with synchronization — Very nuts-and-bolts ¢ So many programmers work with higher level concepts. Sort of like ADTs for synchronization — We'll just look at one example today — There are many others; take cs4410 to learn more ® docsity.com A producer/consumer example  • Thread A produces loaves of bread and puts  them on a shelf with capacity K  – For example, maybe K=10  • Thread B consumes the loaves by taking them  off the shelf  – Thread A doesn’t want to overload the shelf  – Thread B doesn’t wait to leave with empty arms  producer shelves consumer docsity.com Producer/Consumer example  class Bakery { int nLoaves = 0; // Current number of waiting loaves final int K = 10; // Shelf capacity public synchronized void produce() { while(nLoaves == K) this.wait(); // Wait until not full ++nLoaves; this.notifyall(); // Signal: shelf not empty } public synchronized void consume() { while(nLoaves == 0) this.wait(); // Wait until not empty --nLoaves; this.notifyall(); // Signal: shelf not full } } docsity.com Producer/Consumer example  class Bakery { int nLoaves = 0; // Current number of waiting loaves final int K = 10; // Shelf capacity public synchronized void produce() { while(nLoaves == K) this.wait(); // Wait until not full ++nLoaves; this.notifyall(); // Signal: shelf not empty } public synchronized void consume() { while(nLoaves == 0) this.wait(); // Wait until not empty --nLoaves; this.notifyall(); // Signal: shelf not full } } docsity.com Bounded Buffer example  class BoundedBuffer<T> { int putPtr = 0, getPtr = 0; // Next slot to use int available = 0; // Items currently available final int K = 10; // buffer capacity T[] buffer = new T[K]; public synchronized void produce(T item) { while(available == K) this.wait(); // Wait until not full buffer[putPtr++ % K] = item; ++available; this.notifyall(); // Signal: not empty } public synchronized T consume() { while(available == 0) this.wait(); // Wait until not empty --available; T item = buffer[getPtr++ % K]; this.notifyall(); // Signal: not full return item; } } docsity.com Trickier example ¢ Suppose we want to use locking in a BST — Goal: allow multiple threads to search the tree — But don’t want an insertion to cause a search thread to throw an exception docsity.com Safe version: Attempt #1  class BST { Object name; // Name of this node Object value; // Value of associated with that name BST left, right; // Children of this node // Constructor public void BST(Object who, Object what) { name = who; value = what; } // Returns value if found, else null public synchronized Object get(Object goal) { if(name.equals(goal)) return value; if(name.compareTo(goal) < 0) return left==null? null: left.get(goal); return right==null? null: right.get(goal); } // Updates value if name is already in the tree, else adds new BST node public synchronized void put(Object goal, object value) { if(name.equals(goal)) { this.value = value; return; } if(name.compareTo(goal) < 0) { if(left == null) { left = new BST(goal, value); return; } left.put(goal, value); } else { if(right == null) { right = new BST(goal, value); return; } right.put(goal, value); } } } docsity.com Attempt #1 ¢ Just make both put and get synchronized: — public synchronized Object get(...) {... } — public synchronized void put(...) {... } ¢ This works but it kills ALL concurrency — Only one thread can look at the tree at a time ”| — Even if all the threads were doing “get docsity.com Visualizing attempt #1  Cathy  cd4  Freddy  netid: ff1  Martin  mg8  Andy  am7  Zelda  za7  Darleen  dd9  Ernie  gb0  Put(Ernie, eb0) Get(Martin)… must wait! Get(Martin)… resumes docsity.com Attempt #4  • This version is safe: only accesses the shared variables left and  right while holding locks  • In fact it should work (I think)  // Returns value if found, else null public Object get(Object goal) { BST checkLeft = null, checkRight = null; synchronized(this) { if(name.equals(goal)) return value; if(name.compareTo(goal) < 0) { if (left==null) return null; else checkLeft = left; } else { if(right==null) return null; else checkRight = right; } } if (checkLeft != null) return checkleft.get(goal); if (checkRight != null) return checkright.get(goal); /* Never executed but keeps Java happy */ return null; } docsity.com Attempt #3 illustrates risks  • The hardware itself actually needs us to use  locking and attempt 3, although it looks right  in Java, could actually malfunction in various  ways  – Issue: put updates several fields:  • parent.left (or parent.right) for its parent node  • this.left and this.right and this.name and this.value  – When locking is used correctly, multicore  hardware will correctly implement the updates  – But if you look at values without locking, as we did  in Attempt #3, hardware can behave oddly!  docsity.com Why can hardware cause bugs?  • Issue here is covered in cs3410 & cs4410  – Problem is that the hardware was designed under the requirement  that if threads contend to access shared memory, then readers and  writers must use locks  – Solutions #1 and #2 used locks and so they worked, but had no  concurrency  – Solution #3 violated the hardware rules and so you could see various  kinds of garbage in the fields you access!  – Solution #4 should be correct, but perhaps not optimally concurrent  (doesn’t allow concurrency while even one “put” is active)  • It’s hard to design concurrent data structures!  docsity.com More tricky things to know about  • With priorities Java can be very annoying  – ALWAYS runs higher priority threads before lower  priority threads if scheduler must pick  – The lower priority ones might never run at all    • Consequence: risk of a “priority inversion”  – High priority thread t1 is waiting for a lock, t2 has it  – Thread t2 is runnable, but never gets scheduled  because t3 is higher priority and “busy”  docsity.com Debugging concurrent code  • There are Eclipse features to help you debug  concurrent code that uses locking  – These include packages to detect race conditions or  non‐deterministic code paths  – Packages that will track locks in use and print nice  summaries if needed  – Packages for analyzing performance issues  • Heavy locking can kill performance on multicore machines  • Basically, any sharing between threads on different cores is a  performance disaster  docsity.com Summary  Use of multiple processes and multiple threads within  each process can exploit concurrency  Which may be real (multicore) or “virtual” (an illusion)  But when using threads, beware!  Must lock (synchronize) any shared memory to avoid non‐ determinism and race conditions  Yet synchronization also creates risk of deadlocks  Even with proper locking concurrent programs can have other  problems such as “livelock”  Serious treatment of concurrency is a complex topic  (covered in more detail in cs3410 and cs4410)  Nice tutorial at  http://docs.oracle.com/javase/tutorial/essential/concurrency/index.html  docsity.com
Docsity logo



Copyright © 2024 Ladybird Srl - Via Leonardo da Vinci 16, 10126, Torino, Italy - VAT 10816460017 - All rights reserved