73
Deadlock, ReaderWriter problem and Condi6on synchroniza6on Michelle Ku>el

ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

  • Upload
    others

  • View
    0

  • Download
    0

Embed Size (px)

Citation preview

Page 1: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Deadlock,  Reader-­‐Writer  problem  and  Condi6on  synchroniza6on  

Michelle  Ku>el  

Page 2: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Serial  versus  concurrent  

Sequential correctness is mostly concerned with safety properties: –  ensuing that a program transforms each before-state to the

correct after-state.

Concurrent correctness is also concerned with safety, but the problem is much, much harder: –  safety must be ensured despite the vast number of ways

steps of concurrent threads can be be interleaved.

Also,concurrent correctness encompasses a variety of liveness properties that have no counterparts in the sequential world.

Page 3: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Concurrent  correctness  

There  are  two  types  of  correctness  proper6es:  

Safety  proper+es    

 The  property  must  always  be  true.  

Liveness  proper+es    

 The  property  must  eventually  become  true.    

Page 4: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Java  Deadlocks  

We  use  locking  to  ensure  safety  •  but  locks  are  inherently  vulnerable  to  deadlock  

•  indiscriminate  locking  can  cause  lock-­‐ordering  deadlocks  

Page 5: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Dining  philosophers  

Classic  problem  used  to  illustrate  deadlock  –  proposed  by  Dijkstra  in  1965  

•  a  table  with  five  silent  philosophers,  five  plates,  five  forks  (or  chops6cks)  and  a  big  bowl  of  spagheP  (or  rice).  

•  Each  philosopher  must  alternately  think  and  eat.    

•  Ea6ng  is  not  limited  by  the  amount  of  spagheP  leR:  assume  an  infinite  supply.    

•  However,  a  philosophers  need  two  forks  to  eat  

•  A  fork  is  placed  between  each  pair  of  adjacent  philosophers.  

unrealis6c,  unsanitary  and  interes6ng  

Page 6: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Dining  philosophers  

•  Basic  philosopher  loop:  while True:! think()! get_forks()! eat()! put_forks()!

The  problem  is  how  to  design  a  concurrent  algorithm  such  that  each  philosopher  won't  starve,  i.e.  can  forever  con6nue  to  alternate  between  ea6ng  and  thinking.  

•  Some  algorithms  result  in  some  or  all  of  the  philosophers  dying  of  hunger….  deadlock  

Page 7: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Dining  philosophers  in  Java  class Philosopher extends Thread {!!int identity;!!Chopstick left; Chopstick right;!!Philosopher(Chopstick left,Chopstick right){!! !this.left = left; this.right = right;!!}!!public void run() {!! !while (true) {!! ! !try {!! ! ! !sleep(…); // thinking!! ! ! !right.get(); left.get(); // hungry!! ! ! !sleep(…) ; // eating!! ! ! !right.put(); left.put();!! ! !} catch (InterruptedException e) {}!! !}!!}!

}!

poten+al  for  deadlock  

Page 8: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Chops6ck  Monitor  class Chopstick {!!Boolean taken=false;!!synchronized void put() {!! !taken=false;!! !notify();!!}!!synchronized void get() throws!! ! ! ! !InterruptedException!!{!! !while (taken) wait();!! !taken=true;!!}!

}!

Page 9: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Applet  for  diners  

for (int i =0; i<N; ++I) // create Chopsticks!!stick[i] = new Chopstick();!

for (int i =0; i<N; ++i){ !// create Philosophers!!phil[i]=new Philosopher(stick[(i-1+N%N],stick[i]);!!phil[i].start();!

}!

Page 10: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Dining  philosophers  cont.  

We  can  avoid  deadlock  by:  •   controlling  the  number  of  philosophers  (HOW?)  

•  change  the  order  in  which  the  philosophers  pick  up  forks.  (HOW?)  

Page 11: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Mo6va6ng  Deadlock  Issues  Consider  a  method  to  transfer  money  between  bank  accounts    

11  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

class BankAccount { … synchronized void withdraw(int amt) {…} synchronized void deposit(int amt) {…} synchronized void transferTo(int amt, BankAccount a) { this.withdraw(amt); a.deposit(amt); } }

No6ce  during  call  to  a.deposit,  thread  holds  2  locks  

–  Need  to  inves6gate  when  this  may  be  a  problem  

Page 12: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

The  Deadlock  

12  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

acquire lock for x do withdraw from x

block on lock for y

acquire lock for y do withdraw from y

block on lock for x

Thread  1:  x.transferTo(1,y)

Time  

For  simplicity,  suppose  x  and  y  are  sta6c  fields  holding  accounts  

Thread  2:  y.transferTo(1,x)

Page 13: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Deadly  embrace  

Simplest  form  of  deadlock:  •  Thread  A  holds  lock  L  while  trying  to  acquire  lock  M,  while  thread  B  holds  lock  M  while  trying  to  acquire  lock  L.  

Page 14: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Deadlock,  in  general  

A  deadlock  occurs  when  there  are  threads  T1,  …,  Tn  such  that:  

•  For  i=1,..,n-­‐1,  Ti  is  wai6ng  for  a  resource  held  by  T(i+1)  •  Tn  is  wai6ng  for  a  resource  held  by  T1  

In  other  words,  there  is  a  cycle  of  wai6ng  –  Can  formalize  as  a  graph  of  dependencies  

Deadlock  avoidance  in  programming  amounts  to  employing    techniques  to  ensure  a  cycle  can  never  arise  

14  slide  adapted  from:  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

Page 15: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Deadlocks  in  Java  

Java  applica6ons  do  not  recover  from  deadlocks:  •  when  a  set  of  Java  threads  deadlock,  they  are  permanently  out  of  commission  

•  applica6on  may  stall  completely,  a  subsystem  may  stall,  performance  may  suffer  – ….  all  not  good!  

•  If  there  is  poten6al  for  deadlock  it  may  actually  never  happen,  but  usually  does  under  worst  possible  condi6ons  

 so  we  need  to  ensure  that  it  can’t  happen  

Page 16: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Back  to  our  example  Op6ons  for  deadlock-­‐proof  transfer:  

1.  Make  a  smaller  cri6cal  sec6on:  transferTo  not  synchronized  –  Exposes  intermediate  state  aRer  withdraw  before  deposit –  May  be  okay,  but  exposes  wrong  total  amount  in  bank  

2.  Coarsen  lock  granularity:  one  lock  for  all  accounts  allowing  transfers  between  them  –  Works,  but  sacrifices  concurrent  deposits/withdrawals  

3.  Give  every  bank-­‐account  a  unique  number  and  always  acquire  locks  in  the  same  order  –  En/re  program  should  obey  this  order  to  avoid  cycles  –  Code  acquiring  only  one  lock  is  fine  

16  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

Page 17: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Ordering  locks  

17  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

class BankAccount { … private int acctNumber; // must be unique void transferTo(int amt, BankAccount a) { if(this.acctNumber < a.acctNumber) synchronized(this) { synchronized(a) { this.withdraw(amt); a.deposit(amt); }} else synchronized(a) { synchronized(this) { this.withdraw(amt); a.deposit(amt); }} } }

Page 18: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Lock-­‐ordering  deadlocks  

•  occur  when  two  threads  a>empt  to  acquire  the  same  locks  in  a  different  order  

•  A  program  will  be  free  of  lock-­‐ordering  deadlocks  if  all  threads  acquire  the  locks  they  need  in  a  fixed  global  order  –  requires  global  analysis  of  your  programs  locking  behaviour  

•  A  program  than  never  acquires  more  than  one  lock  at  a  +me  will  also  never  deadlock,  but  oRen  imprac6cal  

Page 19: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Another  example  From  the  Java  standard  library  

19  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

class StringBuffer { private int count; private char[] value; … synchronized append(StringBuffer sb) { int len = sb.length(); if(this.count + len > this.value.length) this.expand(…); sb.getChars(0,len,this.value,this.count); } synchronized getChars(int x, int, y, char[] a, int z) { “copy this.value[x..y] into a starting at z” } }

Page 20: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Two  problems  Problem  #1:  The  lock  for  sb  is  not  held  between  calls  to  

sb.length and  sb.getChars    –  So  sb  could  get  longer  –  Would  cause  append  to  throw  an  ArrayBoundsException

Problem  #2:  Deadlock  poten6al  if  two  threads  try  to  append  in  opposite  direc6ons,  just  like  in  the  bank-­‐account  first  example  

Not  easy  to  fix  both  problems  without  extra  copying:  –  Do  not  want  unique  ids  on  every  StringBuffer –  Do  not  want  one  lock  for  all  StringBuffer  objects  

Actual  Java  library:  fixed  neither  (leR  code  as  is;  changed  javadoc)    –  Up  to  clients  to  avoid  such  situa6ons  with  own  protocols  

20  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

Page 21: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Perspec6ve  

•  Code  like  account-­‐transfer  and  string-­‐buffer  append  are  difficult  to  deal  with  for  deadlock  

•  Easier  case:  different  types  of  objects    –  Can  document  a  fixed  order  among  types  –  Example:  “When  moving  an  item  from  the  hashtable  to  the  

work  queue,  never  try  to  acquire  the  queue  lock  while  holding  the  hashtable  lock”  

•  Easier  case:  objects  are  in  an  acyclic  structure  –  Can  use  the  data  structure  to  determine  a  fixed  order  –  Example:  “If  holding  a  tree  node’s  lock,  do  not  acquire  other  

tree  nodes’  locks  unless  they  are  children  in  the  tree”  

21  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

Page 22: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Why  are  Thread.suspend  and  Thread.resume  deprecated?  

Thread.suspend  is  inherently  deadlock-­‐prone.    •  If  the  target  thread  holds  a  lock  on  the  monitor  protec6ng  a  cri6cal  system  resource  when  it  is  suspended,  no  thread  can  access  this  resource  un6l  the  target  thread  is  resumed.    

•  If  the  thread  that  would  resume  the  target  thread  a>empts  to  lock  this  monitor  prior  to  calling  resume,  deadlock  results.    

•  Such  deadlocks  typically  manifest  themselves  as  "frozen"  processes.    

Page 23: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Checkpoint  •  The  BirdsSpo>ed2  class  is  thread  safe.  Is  it  also  deadlock  free?    

public final class BirdsSpotted2 { private long CapeStarling = 0; private long SacredIbis = 0; private long CapeRobinChat = 0;

public synchronized long getStarling() { returnCapeStarling;} public synchronized long getIbis() { returnSacredIbis;} public synchronized long getRobin() { returnCapeRobinChat;}

public synchronized long spottedStarling() {return ++CapeStarling;} public synchronized long spottedIbis() { return ++SacredIbis;} public synchronized long spottedRobin() { return ++CapeRobinChat;} }!

Page 24: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Checkpoint  

•  why  can  we  have  2  separate  locks  here?  

•  why  is  it  desirable?  

public  class  MsLunch  {          private  long  orc  =  0;          private  long  dragon  =  0;          private  Object  orcLock  =  new  Object();          private  Object  dragonLock  =  new  Object();  

       public  void  inc1()  {                  synchronized(orcLock)  {                          orc++;                  }          }  

       public  void  inc2()  {                  synchronized(dragonLock)  {                          dragon++;                  }          }  }  

Page 25: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Checkpoint  

•  why  can  we  have  2  separate  locks  here?  

•  why  is  it  desirable?  

public  class  MsLunch  {          private  long  orc  =  0;          private  long  dragon  =  0;          private  Object  orcLock  =  new  Object();          private  Object  dragonLock  =  new  Object();  

       public  void  inc1()  {                  synchronized(orcLock)  {                          orc++;                  }          }  

       public  void  inc2()  {                  synchronized(dragonLock)  {                          dragon++;                  }          }  }  

Advantage  of  this  using  private  lock:  lock  is  encapsulated  so  client  code  

cannot  acquire  it  –  clients  incorrectly  using  lock  can  

cause  liveness  problems  –  verifying  that  a  publically  

accessible  lock  is  used  properly  requires  examining  the  en6re  program,  compared  to  a  single  class  for  a  private  one  

Page 26: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Progress  Condi6ons      

•  Deadlock-­‐free:  some  thread  trying  to  acquire  the  lock  eventually  succeeds.  

•  Starva/on-­‐free:  every  thread  trying  to  acquire  the  lock  eventually  succeeds.  

Art  of  Mul6processor  Programming   26  

Page 27: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Starva+on  

•  much  less  common  a  problem  than  deadlock  

•  situa6on  where  a  thread  is  unable  to  gain  regular  access  to  shared  resources  and  is  unable  to  make  progress.    –  most  commonly  starved  resource  is  CPU  cycles  

•  happens  when  shared  resources  are  made  unavailable  for  long  periods  by  "greedy"  threads.  

•   For  example:  –  suppose  an  object  provides  a  synchronized  method  that  oRen  

takes  a  long  6me  to  return.    –  If  one  thread  invokes  this  method  frequently,  other  threads  that  

also  need  frequent  synchronized  access  to  the  same  object  will  oRen  be  blocked.  

Page 28: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Starva6on  

•  In  Java  can  be  caused  by  inappropriate  use  of  thread  priori6es  

•  or  indefinite  loops  or  resource  waits  that  do  not  terminate  where  a  lock  is  held  

Page 29: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Livelock  •  A  thread  oRen  acts  in  response  to  the  ac6on  of  another  thread.  

–  If  the  other  thread's  ac6on  is  also  a  response  to  the  ac6on  of  another  thread,  then  livelock  may  result.    

–  As  with  deadlock,  livelocked  threads  are  unable  to  make  further  progress.    

•  Process  is  in  a  livelock  if  it  is  spinning  while  wai6ng  for  a  condi6on  that  will  never  become  true  (busy  wait  deadlock)  

•  comparable  to  two  people  a>emp6ng  to  pass  each  other  in  a  corridor:  Alphonse  moves  to  his  leR  to  let  Gaston  pass,  while  Gaston  moves  to  his  right  to  let  Alphonse  pass.    

•  Seeing  that  they  are  s6ll  blocking  each  other,  Alphone  moves  to  his  right,  while  Gaston  moves  to  his  leR.  They're  s6ll  blocking  each  other,  so...  

Page 30: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Readers/writer  locks  

30  

Page 31: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Reading  vs.  wri6ng  

Recall:  –  Mul6ple  concurrent  reads  of  same  memory:  Not  a  problem  –  Mul6ple  concurrent  writes  of  same  memory:  Problem  –  Mul6ple  concurrent  read  &  write  of  same  memory:  Problem  

So  far:  –  If  concurrent  write/write  or  read/write  might  occur,  use  

synchroniza6on  to  ensure  one-­‐thread-­‐at-­‐a-­‐6me  

But  this  is  unnecessarily  conserva6ve:  –  Could  s6ll  allow  mul6ple  simultaneous  readers!  

31  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

Page 32: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Readers  and  writers  problem  

variant  of  the  mutual  exclusion  problem  where  there  are  two  classes  of  processes:  

•   writers  which  need  exclusive  access  to  resources  

•  readers  which  need  not  exclude  each  other  

Page 33: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Readers/Writers  •  Easy  to  solve  with  mutual  exclusion  •  But  mutual  exclusion  requires  wai6ng  

– One  waits  for  the  other  – Everyone  executes  sequen6ally  

•  Performance  hit!  

Page 34: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Example  

Consider  a  hashtable  with  one  coarse-­‐grained  lock  –  So  only  one  thread  can  perform  opera6ons  at  a  6me  

But  suppose:  –  There  are  many  simultaneous  lookup  opera6ons  – insert  opera6ons  are  very  rare  

Note:  Important  that  lookup  doesn’t  actually  mutate  shared  memory,  like  a  move-­‐to-­‐front  list  opera6on  would  

34  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

Page 35: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Readers/writer  locks  A  new  synchroniza6on  ADT:  The  readers/writer  lock  

•  A  lock’s  states  fall  into  three  categories:  –  “not  held”    –  “held  for  wri6ng”  by  one  thread    –  “held  for  reading”  by  one  or  more  threads  

•  new:  make  a  new  lock,  ini6ally  “not  held”  •  acquire_write:  block  if  currently  “held  for  reading”  or  

“held  for  wri6ng”,  else  make  “held  for  wri6ng”  •  release_write:  make  “not  held”  •  acquire_read:  block  if  currently  “held  for  wri6ng”,  else  

make/keep  “held  for  reading”  and  increment  readers  count  •  release_read:  decrement  readers  count,  if  0,  make  “not  

held”  

35  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

0  ≤  writers  ≤  1  0  ≤  readers  writers*readers==0  

Page 36: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Pseudocode  example  (not  Java)  

36  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

class Hashtable<K,V> { … // coarse-grained, one lock for table RWLock lk = new RWLock(); V lookup(K key) { int bucket = hasher(key); lk.acquire_read(); … read array[bucket] … lk.release_read(); } void insert(K key, V val) { int bucket = hasher(key); lk.acquire_write(); … write array[bucket] …

lk.release_write(); } }

Page 37: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Readers/writer  lock  details  

•  A  readers/writer  lock  implementa6on  (“not  our  problem”)  usually  gives  priority  to  writers:  –  Once  a  writer  blocks,  no  readers  arriving  later  will  get  the  lock  

before  the  writer  –  Otherwise  an  insert  could  starve  

•  Re-­‐entrant?  Mostly  an  orthogonal  issue  –  But  some  libraries  support  upgrading  from  reader  to  writer  

•  Why  not  use  readers/writer  locks  with  more  fine-­‐grained  locking,  like  on  each  bucket?  –  Not  wrong,  but  likely  not  worth  it  due  to  low  conten6on  

37  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

Page 38: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

In  Java  

Java’s  synchronized  statement  does  not  support  readers/writer  

Instead,  library    java.util.concurrent.locks.ReentrantReadWriteL

ock

•  Different  interface:  methods  readLock  and  writeLock  return  objects  that  themselves  have  lock  and  unlock  methods  

•  Does  not  have  writer  priority  or  reader-­‐to-­‐writer  upgrading  –  Always  read  the  documenta6on  

38  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

Page 39: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Condi6on  variables  

39  

Page 40: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Condi6on  variables:  Producer-­‐Consumer  synchroniza6on  problem  

In  mul6threaded  programs  there  is  oRen  a  division  of  labor  between  threads.    

•  In  one  common  pa>ern,  some  threads  are  producers  and  some  are  consumers.  – Producers  create  items  of  some  kind  and  add  them  to  a  data  structure;    

– consumers  remove  the  items  and  process  them  

•  a  new  coordina6on  problem:  Producer-­‐Consumer  

Page 41: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Producer-­‐Consumer  

canonical  example  of  a  bounded  buffer  for  sharing  work  among  threads  

Bounded  buffer:  A  queue  with  a  fixed  size  –  (Unbounded  s6ll  needs  a  condi6on  variable,  but  1  instead  of  2)  

For  sharing  work  –  think  an  assembly  line:    –  Producer  thread(s)  do  some  work  and  enqueue  result  objects  –  Consumer  thread(s)  dequeue  objects  and  do  next  stage  –  Must  synchronize  access  to  the  queue  

41  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

f   e   d   c  buffer  

back   front  

producer(s)  enqueue  

consumer(s)  dequeue  

Page 42: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

The little book of semaphores, by Downey 42  

Producer-­‐consumer  problem  

Event-­‐driven  programs  are  a  good  example.    •  Whenever  an  event  occurs,  a  producer  thread  creates  an  event  object  and  adds  it  to  the  event  buffer.  

•   Concurrently,  consumer  threads  take  events  out  of  the  buffer  and  process  them.  

Page 43: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

43  

Producer-­‐consumer  problem  

For  this  to  work  correctly:  •  Producers  must  not  produce  when  the  buffer  is  full  –  must  wait  6ll  there  is  a  gap.  

•  Consumers  must  not  consume  when  the  buffer  is  empty  –  must  wait  6ll  it  is  filled.  

Page 44: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Code,  a>empt  1  

44  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

class Buffer<E> { E[] array = (E[])new Object[SIZE]; … // front, back fields, isEmpty, isFull methods synchronized void enqueue(E elt) { if(isFull()) ??? else … add to array and adjust back … } synchronized E dequeue() if(isEmpty()) ??? else … take from array and adjust front … } }

Page 45: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Wai6ng  •  enqueue  to  a  full  buffer  should  not  raise  an  excep6on  

–  Wait  un6l  there  is  room  

•  dequeue  from  an  empty  buffer  should  not  raise  an  excep6on  –  Wait  un6l  there  is  data  

Bad  approach  is  to  spin  (wasted  work  and  keep  grabbing  lock)  

45  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

void enqueue(E elt) { while(true) { synchronized(this) { if(isFull()) continue; … add to array and adjust back … return; }}} // dequeue similar

Page 46: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

What  we  want  •  Be>er  would  be  for  a  thread  to  wait  un6l  it  can  proceed    

–  Be  no/fied  when  it  should  try  again  –  In  the  mean6me,  let  other  threads  run  

•  Like  locks,  not  something  you  can  implement  on  your  own  –  Language  or  library  gives  it  to  you,  typically  implemented  with  

opera6ng-­‐system  support  

•  An  ADT  that  supports  this:  condi6on  variable  –  Informs  waiter(s)  when  the  condi/on  that  causes  it/them  to  wait  

has  varied  

•  Terminology  not  completely  standard;  will  mostly  s6ck  with  Java  

46  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

Page 47: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Java  approach:  not  quite  right  

47  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

class Buffer<E> { … synchronized void enqueue(E elt) { if(isFull()) this.wait(); // releases lock and waits add to array and adjust back if(buffer was empty) this.notify(); // wake somebody up } synchronized E dequeue() { if(isEmpty()) this.wait(); // releases lock and waits take from array and adjust front if(buffer was full) this.notify(); // wake somebody up } }

Page 48: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Key  ideas  •  Java  weirdness:  every  object  “is”  a  condi6on  variable  (and  

a  lock)  –  other  languages/libraries  oRen  make  them  separate  

•  wait:    –  “register”  running  thread  as  interested  in  being  woken  up  –  then  atomically:  release  the  lock  and  block  –  when  execu6on  resumes,  thread  again  holds  the  lock  

•  notify: –  pick  one  wai6ng  thread  and  wake  it  up  –  no  guarantee  woken  up  thread  runs  next,  just  that  it  is  no  

longer  blocked  on  the  condi/on  –  now  wai6ng  for  the  lock  –  if  no  thread  is  wai6ng,  then  do  nothing  

48  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

Page 49: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Bug  #1  

Between  the  6me  a  thread  is  no6fied  and  it  re-­‐acquires  the  lock,  the  condi6on  can  become  false  again!  

49  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

synchronized void enqueue(E elt){ if(isFull()) this.wait(); add to array and adjust back … }

if(isFull()) this.wait();

add to array

Time  

Thread  2  (dequeue)  Thread  1  (enqueue)  

take from array if(was full)

this.notify();

make full again

Thread  3  (enqueue)  

Page 50: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Bug  fix  #1  

Guideline:  Always  re-­‐check  the  condi6on  aRer  re-­‐gaining  the  lock  –  In  fact,  for  obscure  reasons,  Java  is  technically  allowed  to  no6fy  a  

thread  spuriously  (i.e.,  for  no  reason)  

50  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

synchronized void enqueue(E elt) { while(isFull()) this.wait(); … } synchronized E dequeue() { while(isEmpty()) this.wait(); … }

Page 51: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Bug  #2  •  If  mul6ple  threads  are  wai6ng,  we  wake  up  only  one  

–  Sure  only  one  can  do  work  now,  but  can’t  forget  the  others!  

51  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

while(isFull()) this.wait();

Time  

Thread  2  (enqueue)  Thread  1  (enqueue)  

// dequeue #1 if(buffer was full) this.notify();

// dequeue #2 if(buffer was full) this.notify();

Thread  3  (dequeues)  while(isFull()) this.wait();

Page 52: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Bug  fix  #2  

notifyAll  wakes  up  all  current  waiters  on  the  condi6on  variable  

Guideline:  If  in  any  doubt,  use  notifyAll    –  Wasteful  waking  is  be>er  than  never  waking  up  

•  So  why  does  notify  exist?  –  Well,  it  is  faster  when  correct…  

52  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

synchronized void enqueue(E elt) { … if(buffer was empty) this.notifyAll(); // wake everybody up } synchronized E dequeue() { … if(buffer was full) this.notifyAll(); // wake everybody up }

Page 53: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

A  new  liveness  hazard:  missed  signals  

•  A  missed  signal  occurs  when  a  thread  must  wait  for  a  specific  condi6on  that  is  already  true,  but  fails  to  check  before  wai6ng  

•  no6fyAll  is  almost  always  be>er  than  no6fy,  because  it  is  less  prone  to  missed  signals    

Page 54: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Alternate  approach  •  An  alterna6ve  is  to  call  notify  (not  notifyAll)  on  every  enqueue  /  dequeue,  not  just  when  the  buffer  was  empty  /  full  –  Easy:  just  remove  the  if  statement  

•  Alas,  makes  our  code  subtly  wrong  since  it’s  technically  possible  that  an  enqueue  and  a  dequeue  are  both  wai6ng.  –  See  notes  for  the  step-­‐by-­‐step  details  of  how  this  can  happen  

•  Works  fine  if  buffer  is  unbounded  since  then  only  dequeuers  wait  

54  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

Page 55: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Alternate  approach  fixed  •  The  alternate  approach  works  if  the  enqueuers  and  dequeuers  wait  

on  different  condi6on  variables  –  But  for  mutual  exclusion  both  condi6on  variables  must  be  associated  

with  the  same  lock  

•  Java’s  “everything  is  a  lock  /  condi6on  variable”  doesn’t  support  this:  each  condi6on  variable  is  associated  with  itself  

•  Instead,  Java  has  classes  in  java.util.concurrent.locks  for  when  you  want  mul6ple  condi6ons  with  one  lock  –  class ReentrantLock  has  a  method  newCondition  that  

returns  a  new  Condition  object  associate  with  the  lock  –  See  the  documenta6on  if  curious  

55  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

Page 56: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Last  condi6on-­‐variable  comments  

•  notify/notifyAll oRen  called signal/broadcast,  also  called pulse/pulseAll

•  Condi6on  variables  are  subtle  and  harder  to  use  than  locks  •  But  when  you  need  them,  you  need  them    

–  Spinning  and  other  work-­‐arounds  don’t  work  well  

•  Fortunately,  like  most  things  in  a  data-­‐structures  course,  the  common  use-­‐cases  are  provided  in  libraries  wri>en  by  experts  –  Example:    java.util.concurrent.ArrayBlockingQueue<E>

–  All  uses  of  condi6on  variables  hidden  in  the  library;  client  just  calls  put  and  take

56  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

Page 57: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Condi6on  synchroniza6on  

Java  has  built-­‐in  mechanisms  for  wai6ng  for  a  condi6on  to  become  true:    wait()  and  notify()!

They are tightly bound to intrinsic locking and can be difficult to use correctly

Often easier to use existing synchronizer classes: •  coordinate control flow of cooperating threads

e.g. BlockingQueue and Semaphore!

Page 58: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Java  Blocking  queues  and  the  producer-­‐consumer  design  paGern  

•  BlockingQueue  extends  Queue  with  blocking  inser6on  and  retrieval  opera6ons  – put  and  take  methods    – 6med  equivalents:  offer  and  poll!

•  If  queue  is  empty,  a  retrieval  (take)  blocks  un6l  an  element  is  available  

•  If  queue  is  full  (for  bounded  queues),  inser6on  (put)  blocks  un6l  there  is  space  available  

Page 59: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Producer-­‐consumer  design  pa>ern  

separates  iden6fica6on  of  work  to  be  done  from  execu6on  of  that  work  

•  work  items  are  placed  on  “to  do”  list  for  later  processing  

•  removes  code  dependencies  between  producers  and  consumers  

Most  common  design  is  a  thread  pool  coupled  with  a  work  queue  

Page 60: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Several  implementa6ons  of  blocking  queue  

•  LinkedBlockingQueue,  ArrayBlockingQueue:  – FIFO  queues  

•  Priority  blocking  queue  •  SynchronousQueue:  

– queued  THREADS  

Page 61: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

The  Executor  Framework  and  Thread  Pools  

•  usually  the  easiest  way  to  implement  a  producer-­‐consumer  design  is  to  use  a  thread  pool  implementa6on  as  part  of  the  Executor  framework  public interface Executor {!!void execute(Runnable command);!}!

An  Executor  object  typically  creates  and  manages  a  group  of  threads  called  a  thread  pool  

•  threads  execute  the  Runnable  objects  passed  to  the  execute  method  

Page 62: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Concurrency  summary  •  Access  to  shared  resources  introduces  new  kinds  of  

bugs  –  Data  races  –  Cri6cal  sec6ons  too  small  –  Cri6cal  sec6ons  use  wrong  locks  –  Deadlocks  

•  Requires  synchroniza6on  –  Locks  for  mutual  exclusion  (common,  various  flavors)  –  Condi6on  variables  for  signaling  others  (less  common)    

•  Guidelines  for  correct  use  help  avoid  common  pi{alls  

•  Not  clear  shared-­‐memory  is  worth  the  pain  –  But  other  models  (e.g.,  message  passing)  not  a  panacea  

62  Sophomoric  Parallelism  &  

Concurrency,  Lecture  6  

Page 63: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Java  synchronizers  

A  synchronizer  is  any  object  that  coordinates  the  control  flow  of  threads  based  on  its  state.    Java  has:  – Blocking  Queues  – Semphores  – Barriers  – Latches  

Page 64: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Java  synchronizers  

All  synchronizers:  •  determine  whether  arriving  threads  should  be  allowed  to  pass  or  be  forced  to  wait  based  on  encapsulated  state  

•  provide  methods  to  manipulate  state  

•  provide  methods  to  wait  efficiently  for    the  synchronizers  to  enter  the  desired  state  

Page 65: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Latches  

Acts  as  a  gate:  no  thread  can  pass  un6l  the  gate  opens,  and  then  all  can  pass  

•  delays  progress  of  threads  un6l  it  enters  terminal  state  

•  cannot  then  change  state  again  (open  forever)    For  example,  can  be  used  to  wait  un6l  all  par6es  involved  in  an  ac6vity  are  ready  to  proceed:  –  like  all  players  in  a  mul6-­‐player  game  

Page 66: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

CountDownLatch!

CountDownLatch allows  one  or  more  threads  to  wait  for  a  set  of  events  to  occur  

Latch  state  is  a  counter  ini6alized  to  a  posi6ve  number,  represen6ng  number  of  elements  to  wait  for  

Page 67: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Semaphores  

Coun+ng  semaphores  are  used  to  control  the  number  of  ac6vi6es  that  can  access  a  certain  resource  or  perform  a  given  ac6on  at  the  same  6me  

•  like  a  set    of  virtual  permits  – ac6vi6es  can  acquire  permits  and  release  then  when  they  are  done  with  them  

•  Useful  for  implemen6ng  resource  pools,  such  as  database  connec6on  pools.  

Page 68: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Barriers  

Similar  to  latches  –  block  a  group  of  threads  un6l  an  event  has  occurred  –  but:  

•  latches  wait  for  events  •  barriers  wait  for  other  threads  

Page 69: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

CyclicBarrier  

Allows  a  fixed  number  of  par6es  to  rendezvous  repeatedly  at  a  barrier  point  

Threads  call  await  when  they  reach  the  barrier  point  and  await  blocks  un6l  all  threads  have  reached  the  barrier  point.  

Once  all  threads  are  there,  the  barrier  is  passed,  all  threads  are  released  and  the  barrier  is  reset.  

Page 70: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

CyclicBarrier  

Useful  in  parallel  itera6ve  algorithms  that  break  down  a  problem  into  a  fixed  number  of  independent  subproblems:  

•  In  many  simula6ons,  the  work  done  in  one  step  can    be  done  in  parallel,  but  all  work  in  one  step  must  be  completed  before  the  next  step  begins…  

Page 71: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Conway’s  game  of  life  Conway’s  game  of  life  is  a  cellular  automaton  first  proposed  by  the  

Bri6sh  mathema6cian  John  Horton  Conway  in  1970.    The  game  is  a  simula6on  on  a  two-­‐dimensional  grid  of  cells.  Each  cell  

starts  off  as  either  alive  or  dead.  The  state  of  the  cell  changes  depending  on  the  state  of  its    7  neighbours  in  the  grid.  At  each  6me-­‐step,  we  update  the  state  of  each  cell  according  to  the  following  four  rules.  

•  A  live  cell  with  fewer  than  two  live  neighbors  dies  due  to  underpopula6on.  

•  A  live  cell  with  more  than  three  live  neighbors  dies  due  to  overpopula6on.  

•  A  live  cell  with  two  or  three  live  neighbors  survives  to  the  next  genera6on.  

•  A  dead  cell  with  exactly  three  live  neighbors  becomes  a  live  cell  due  to  breeding.  

Page 72: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Mul6threaded  Conway’s  game  of  life  

Parallel  program  generates  threads  equal  to  the  number  of  cells,    •  or,  be>er,  a  part  of  the  grid  -­‐  and  updates  the  status  of  each  cell  independently.  •  Before  proceeding  to  the  next  6me  step,  it  is  necessary  that  all  the  

grids  have  been  updated.    •  This  requirement  can  be  ensured  by  using  a  global  barrier  for  all  

threads.  

Page 73: ParallelJava 2012 8 ConditionDeadlockWR · Serial*versus*concurrent Sequential correctness is mostly concerned with safety properties: – ensuing that a program transforms each before-state

Causes  of  Efficiency  Problems  in  Java  

Too  much  locking  •  Cost  of  using  synchronized  •  Cost  of  blocking  wai6ng  for  locks  •  Cost  of  thread  cache  flushes  and  reloads  Too  many  threads  •  Cost  of  star6ng  up  new  threads  •  Cost  of  context  switching  and  scheduling  •  Cost  of  inter-­‐CPU  communica6on,  cache  misses  Too  much  coordina+on  •  Cost  of  guarded  waits  and  no6fica6on  messages  •  Cost  of  layered  concurrency  control  Too  many  objects  •  Cost  of  using  objects  to  represent  state,  messages,  etc