NWEN303 Concurrent Programming
14: Model solutions for Ass2(universe) and Model solutions for MockTermTest
Marco Servetto VUW
●
Ass2 universe
Gui.java already has a minimal form of parallelism.
[..] schedulerRepaint = new ScheduledThreadPoolExecutor(1); [..] schedulerSimulation = new ScheduledThreadPoolExecutor(1);
… schedulerRepaint.scheduleAtFixedRate(()->{
if(!schedulerRepaint.getQueue().isEmpty()){..return;}
try {SwingUtilities.invokeAndWait(()->repaint());}
catch (InvocationTargetException | InterruptedException e) {..} },
500,5, TimeUnit.MILLISECONDS);
…
/*inside paint:*/ for(DrawableParticle p: m.pDraw){p.draw(g);} …
private void updateGraphicalRepresentation() {
ArrayList
for(Particle p:this.p){//the whole ROG of d is freshly created!
d.add(new DrawableParticle((int)p.x, (int)p.y, (int)Math.sqrt(p.mass),c )); }
this.pDraw=d;//atomic update }
Ass2 universe Make model parallel
public class ModelParallel extends Model{//only override step and mergeParticles public void step() {
this.p.parallelStream().forEach(p->p.interact(this));//the most important bit this.p.parallelStream().forEach(p->p.move(this));//trivial but little gain mergeParticles();
updateGraphicalRepresentation();//do not exagerate, too much is bad
}
public void mergeParticles(){
Stack
Particle current=deadPs.pop();
Set
newPs.add(()->mergeParticles(ps));//add computation to do later
}
newPs.parallelStream()
.map(s->s.get())// doing mergeParticles(ps)
.sequential()//to ensure the order is correct and no need to sync this.p .forEach(p->this.p.add(p));
} }
●
Ass2 universe Make model parallel
public void step() { this.p.parallelStream().forEach(p->p.interact(this));//the most important bit this.p.parallelStream().forEach(p->p.move(this));//trivial but little gain mergeParticles();
updateGraphicalRepresentation();//do not exagerate, too much is bad
}
●
Ass2 universe Make model parallel
public void mergeParticles(){
Stack
for(Particle p:this.p){if(!p.impacting.isEmpty()){deadPs.add(p);}} this.p.removeAll(deadPs);//very hard to parallelize, and will have little gain
List
Particle current=deadPs.pop();
Set
newPs.add(()->mergeParticles(ps));//add computation to do later }
newPs.parallelStream()
.map(s->s.get())// doing mergeParticles(ps)
.sequential()//to ensure the order is correct and no need to sync this.p .forEach(p->this.p.add(p));
}
//Ideally, we would also like to parallelize this
Set
//but it is iper-hard and I’m not sure how much benefit can be obtained
●
Ass2 universe Make model parallel
newPs.parallelStream()
.map(s->s.get())// doing mergeParticles(ps)
.sequential()//to ensure the order is correct and no need to sync this.p .forEach(p->this.p.add(p));
Without the .sequential() it would be wrong in 2 different ways:
– The order of insertion would be random (so no equal behaviour)
– p is declared as
public List
ArrayList is NOT syncronized, so adding to p from multiple workers could corrupt p.
– .sequential().forEach(..add..) is likely much faster then collecting to another list and
then doing an addAll
●
●
–
● ●
Ass2 universe Test correctness and speed
Do not test with the gui!
Just run a for loop with a Model and a ModelParallel;
each loop iteration do .step() on both,and then check that the two models are identical after each step.
●
If you add an ‘equals’ to ‘Particle’, then some operations like deadPs.removeAll(ps) will use it, possibly making the behaviour different/slower. Thus, it may be better to define your own ‘modelEqual(Model) that checks the particles one by one.
HashSet have nondeterinistic iterators, so if you want to test for equality, even between two sequential models, you must change the HashSet to LinkedHashSet or another deterministic Set.
Double/Floats may behave funny anyway; consider learning about ‘strictfp’ !!
●
●
Ass2 universe
●
Mock Term Test Question 1 a-b-c-d: on the handout
Mock Term Test
//The order of the for MATTERS!
//for(Image i:movie){for(Filter f:filters){}} //for(Filter f:filters){for(Image i:movie){}}
//here we assume every Image in the movie to be a different object //streams solution
void applyFiltersStreams(ArrayList
movie.parallelStream().forEach(i->f.applyFilter(i));
//parallele for, for all the images
} }
Mock Term Test
//The order of the for MATTERS!
//for(Image i:movie){for(Filter f:filters){}} //for(Filter f:filters){for(Image i:movie){}}
//here we assume every Image in the movie to be a different object //futures solution
private static final ExecutorService pool=Executors.newCachedThreadPool(); void applyFiltersFut(ArrayList
throws InterruptedException, ExecutionException{ for(Filter f:filters){//for 1: start the workers
ArrayList
for(Image i:movie){//just swap lines results.add(pool.submit(()->f.applyFilter(i))); }
//for 2: collect the results
for(Future> r:results){r.get();} }
}
//Can still be improved!
Mock Term Test
//first streams solution
//for each Image, we parallelized the application of a single filter,
//we repeated that for all filters.
void applyFiltersStreams(ArrayList
for(Filter f:filters){ movie.parallelStream().forEach(i->f.applyFilter(i));
} }
//We show now how to parallelize the application of all //the filters at the same time.
//It is a bigger operation;
//parallelizing something bigger is better then parallelizing //something smaller.
void applyFiltersStreams(ArrayList
for(Filter f:filters){f.applyFilter(i);}
}); }
●
Mock Term Test Question 2 bad code: the good version
public boolean fix(Product product){
Stream
var msgs=Stream.concat(des,product.reviews.stream()).parallel()
.filter(this::polite) .map(this::fixGrammar) .collect(Collectors.toList());
if(msgs.size()!=product.reviews.size()+1){return false;} product.description=msgs.get(0); product.reviews=msgs.subList(1,msgs.size());
return true;
}
●
–
Mock Term Test Question 2 bad code: bugs in the wrong
BUG1: Survives an InterruptedException without
stopping the computation or resetting the interrupted flag … catch (InterruptedException e) { … e.printStackTrace(); } …
BUG2: attempts to mutate the content of the reviews field, but by being a List and not an ArrayList, it may be not mutable and thus those two lines may cause a runtime exception for a valid Product instance.
productId.reviews.clear(); productId.reviews.addAll(reviews);
–
class ComplexCode {
public boolean polite(String s){/*..*/}
public String fixGrammar(String s){/*..*/}
//the grammar of a polite Product is fixed, and true is returned.
//if the product is not polite, false is returned and the product is not modified. public boolean fix(Product productId){
String description;
ArrayList
ArrayList
for(int i=0;i
ArrayList
for(int i=0;i
ArrayList
for(int i=0;i
ArrayList
for(int i=0;i
ArrayList
for(int i=0;i
ArrayList
for(int i=0;i
ArrayList
for(int i=0;i
ArrayList
for(int i=0;i
ArrayList
for(int i=0;i
ArrayList
for(int i=0;i
ArrayList
for(int i=0;i
ArrayList
for(int i=0;i
String description=product.description; ArrayList
String description=product.description; ArrayList
ArrayList
ArrayList
ArrayList
ArrayList
ArrayList
ArrayList
ArrayList
ArrayList
ArrayList
ArrayList
ArrayList
return true; }
public boolean fix(Product product){
Question 8
ArrayList
ArrayList
return true; }
public boolean fix(Product product){
ArrayList
String current=reviews.get(j);
if(polite(current)){ reviews.set(j,fixGrammar(current)); } else{ reviews.set(j,tag); }
});
koolParrallel.start();
kools.add(koolParrallel);
}
if(!polite(product.description)){ return false; } String description=fixGrammar(product.description); for(Thread t:kools){
try { t.join(); }
catch (InterruptedException e){ e.printStackTrace(); } }
if(reviews.stream().anyMatch(s->s==tag)){return false;} product.description=description; product.reviews.clear(); product.reviews.addAll(reviews);
return true; }
Question 8 bad code: Normalization
ArrayList
We can use a lambda instead of an anonymous nested class
public boolean fix(Product product){
Question 8 bad code: Normalization
null would also be a good tag, since we know is not in the input
ArrayList
ArrayList
String current=reviews.get(j);
if(polite(current)){ reviews.set(j,fixGrammar(current)); } else{ reviews.set(j,tag); }
});
koolParrallel.start();
kools.add(koolParrallel);
}
if(!polite(product.description)){ return false; } String description=fixGrammar(product.description); for(Thread t:kools){
try { t.join(); }
catch (InterruptedException e){ e.printStackTrace(); } }
if(reviews.stream().anyMatch(s->s==tag)){return false;} product.description=description; product.reviews.clear(); product.reviews.addAll(reviews);
return true; }
public boolean fix(Product product){
ArrayList
for(int i=0;i
String current=reviews.get(j);
if(polite(current)){ reviews.set(j,fixGrammar(current)); } else{ reviews.set(j,tag); }
});
koolParrallel.start();
kools.add(koolParrallel);
}
if(!polite(product.description)){ return false; } String description=fixGrammar(product.description); for(Thread t:kools){
try { t.join(); }
catch (InterruptedException e){ e.printStackTrace(); } }
if(reviews.stream().anyMatch(s->s==null)){return false;} product.description=description; product.reviews.clear(); product.reviews.addAll(reviews);
return true; }
Question 8 bad code: Normalization
null would also be a good tag, since we know is not in the input
public boolean fix(Product product){
ArrayList
for(int i=0;i
String current=reviews.get(j);
if(polite(current)){ reviews.set(j,fixGrammar(current)); } else{ reviews.set(j,tag); }
});
koolParrallel.start();
kools.add(koolParrallel);
}
if(!polite(product.description)){ return false; } String description=fixGrammar(product.description); for(Thread t:kools){
try { t.join(); }
catch (InterruptedException e){ e.printStackTrace(); } }
if(reviews.stream().anyMatch(s->s==null)){return false;} product.description=description; product.reviews.clear(); product.reviews.addAll(reviews);
return true; }
Question 8 bad code: Normalization
As discussed, clear+addAll could fail. Lets just use assignment!
public boolean fix(Product product){
ArrayList
for(int i=0;i
String current=reviews.get(j);
if(polite(current)){ reviews.set(j,fixGrammar(current)); } else{ reviews.set(j,tag); }
});
koolParrallel.start();
kools.add(koolParrallel);
}
if(!polite(product.description)){ return false; } String description=fixGrammar(product.description); for(Thread t:kools){
try { t.join(); }
catch (InterruptedException e){ e.printStackTrace(); } }
if(reviews.stream().anyMatch(s->s==null)){return false;} product.description=description; product.reviews=reviews;
return true;
}
Question 8 bad code: Normalization
As discussed, clear+addAll could fail. Lets just use assignment!
public boolean fix(Product product){
ArrayList
for(int i=0;i
String current=reviews.get(j);
if(polite(current)){ reviews.set(j,fixGrammar(current)); } else{ reviews.set(j,tag); }
});
koolParrallel.start();
kools.add(koolParrallel);
}
if(!polite(product.description)){ return false; } String description=fixGrammar(product.description); for(Thread t:kools){
try { t.join(); }
catch (InterruptedException e){ e.printStackTrace(); } }
if(reviews.stream().anyMatch(s->s==null)){return false;} product.description=description; product.reviews=reviews;
return true;
}
Question 8 bad code: Normalization
Terrible names
public boolean fix(Product product){
ArrayList
for(int i=0;i
String current=reviews.get(j);
if(polite(current)){ reviews.set(j,fixGrammar(current)); } else{ reviews.set(j,tag); }
});
t.start();
tasks.add(t);
}
if(!polite(product.description)){ return false; } String description=fixGrammar(product.description); for(Thread t:tasks){
try { t.join(); }
catch (InterruptedException e){ e.printStackTrace(); } }
if(reviews.stream().anyMatch(s->s==null)){return false;} product.description=description; product.reviews=reviews;
return true;
}
Question 8 bad code: Normalization
Terrible names
public boolean fix(Product product){
ArrayList
for(int i=0;i
Question 8 bad code: Normalization
CompletableFutures instead of Thread?
String current=reviews.get(j);
if(polite(current)){ reviews.set(j,fixGrammar(current)); } else{ reviews.set(j,tag); }
});
t.start();
tasks.add(t);
}
if(!polite(product.description)){ return false; } String description=fixGrammar(product.description); for(Thread t:tasks){
try { t.join(); }
catch (InterruptedException e){ e.printStackTrace(); } }
if(reviews.stream().anyMatch(s->s==null)){return false;} product.description=description; product.reviews=reviews;
return true;
}
public boolean fix(Product product){
ArrayList
for(int i=0;i
String current=reviews.get(j); if(polite(current)){reviews.set(j,fixGrammar(current));} else{reviews.set(j,null);}
});
tasks.add(t);
}
if(!polite(product.description)){ return false; } String description=fixGrammar(product.description); for(Thread t:tasks){ t.join(); } if(reviews.stream().anyMatch(s->s==null)){return false;} product.description=description; product.reviews=reviews;
return true;
}
Question 8 bad code: Normalization
CompletableFutures instead of Thread? This join does not even throw InterruptedException
Question 8 bad code: Normalization
public boolean fix(Product product){
ArrayList
for(int i=0;i
String current=reviews.get(j); if(polite(current)){reviews.set(j,fixGrammar(current));} else{reviews.set(j,null);}
});
tasks.add(t);
}
if(!polite(product.description)){ return false; }
String description=fixGrammar(product.description); for(Thread t:tasks){ t.join(); } if(reviews.stream().anyMatch(s->s==null)){return false;} product.description=description; product.reviews=reviews;
return true;
}
After those AUTOMATIC transformations the code is manageable. How to do better?
Question 8 bad code: Normalization
public boolean fix(Product product){
ArrayList
for(int i=0;i
String current=reviews.get(j); if(polite(current)){reviews.set(j,fixGrammar(current));} else{reviews.set(j,null);}
});
tasks.add(t);
}
if(!polite(product.description)){ return false; }
String description=fixGrammar(product.description); for(Thread t:tasks){ t.join(); } if(reviews.stream().anyMatch(s->s==null)){return false;} product.description=description; product.reviews=reviews;
return true;
}
Idea: both description and reviews are just strings, and we handle them the same. Can I just add the description as an extra review?
Question 8 bad code: Normalization
public boolean fix(Product product){
ArrayList
var tasks=new ArrayList
for(int i=0;i
String current=reviews.get(j); if(polite(current)){reviews.set(j,fixGrammar(current));} else{reviews.set(j,null);}
});
tasks.add(t);
}
for(var t:tasks){t.join();} if(reviews.stream().anyMatch(s->s==null)){return false;} product.description=reviews.get(reviews.size()-1); product.reviews=reviews.sublist(0,reviews.size()-1); return true;
}
Now more automatic transformations are possible!!
Question 8 bad code: Normalization
public boolean fix(Product product){
ArrayList
var tasks=new ArrayList
for(int i=0;i
String current=reviews.get(j); if(polite(current)){reviews.set(j,fixGrammar(current));} else{reviews.set(j,null);}
});
tasks.add(t);
}
for(var t:tasks){t.join();} if(reviews.stream().anyMatch(s->s==null)){return false;} product.description=reviews.get(reviews.size()-1); product.reviews=reviews.sublist(0,reviews.size()-1); return true;
}
Since we join directly after composing the list of tasks, we can use streams instead!
Question 8 bad code: Normalization
public boolean fix(Product product){
List
if(polite(current)){return fixGrammar(current);} else{return null;} }).collect(Collectors.toList());
if(reviews.stream().anyMatch(s->s==null)){return false;} product.description=reviews.get(reviews.size()-1); product.reviews=reviews.sublist(0,reviews.size()-1); return true;
}
Since we join directly after composing the list of tasks, we can use streams instead!
Question 8 bad code: Normalization
public boolean fix(Product product){
List
if(polite(current)){return fixGrammar(current);} else{return null;} }).collect(Collectors.toList());
if(reviews.stream().anyMatch(s->s==null)){return false;} product.description=reviews.get(reviews.size()-1); product.reviews=reviews.sublist(0,reviews.size()-1); return true;
}
Now we can normalize the if in the lambda
Question 8 bad code: Normalization
public boolean fix(Product product){
List
if(!polite(current)){return null;} return fixGrammar(current);} }).collect(Collectors.toList());
if(reviews.stream().anyMatch(s->s==null)){return false;} product.description=reviews.get(reviews.size()-1); product.reviews=reviews.sublist(0,reviews.size()-1); return true;
}
Now we can normalize the if in the lambda
Question 8 bad code: Normalization
public boolean fix(Product product){
List
if(!polite(current)){return null;} return fixGrammar(current);} }).collect(Collectors.toList());
if(reviews.stream().anyMatch(s->s==null)){return false;} product.description=reviews.get(reviews.size()-1); product.reviews=reviews.sublist(0,reviews.size()-1); return true;
}
we can replace anyMatch with contains(null)
Question 8 bad code: Normalization
public boolean fix(Product product){
List
if(!polite(current)){return null;} return fixGrammar(current);} }).collect(Collectors.toList());
if(reviews.contains(null)){return false;} product.description=reviews.get(reviews.size()-1); product.reviews=reviews.sublist(0,reviews.size()-1); return true;
}
we can replace anyMatch with contains(null)
Question 8 bad code: Normalization
public boolean fix(Product product){
List
if(!polite(current)){return null;} return fixGrammar(current);} }).collect(Collectors.toList());
if(reviews.contains(null)){return false;} product.description=reviews.get(reviews.size()-1); product.reviews=reviews.sublist(0,reviews.size()-1); return true;
}
Creating an ArrayList just to stream on it once is sad. Start with streams!
Question 8 bad code: Normalization
public boolean fix(Product product){
Stream
if(!polite(current)){return null;} return fixGrammar(current);} }).collect(Collectors.toList());
if(reviews.contains(null)){return false;} product.description=reviews.get(reviews.size()-1); product.reviews=reviews.sublist(0,reviews.size()-1); return true;
}
Creating an ArrayList just to stream on it once is sad. Start with streams!
Question 8 bad code: Normalization
public boolean fix(Product product){
Stream
if(!polite(current)){return null;} return fixGrammar(current);} }).collect(Collectors.toList());
if(reviews.contains(null)){return false;} product.description=reviews.get(reviews.size()-1); product.reviews=reviews.sublist(0,reviews.size()-1); return true;
}
Collecting nulls is sad. Can we just filter them out and then check the size of the result?
Question 8 bad code: Normalization
public boolean fix(Product product){
Stream
.filter(current->polite(current))
.map(current->fixGrammar(current))
.collect(Collectors.toList());
if(reviews.size()!=product.reviews.size()+1){return false;} product.description=reviews.get(reviews.size()-1); product.reviews=reviews.sublist(0,reviews.size()-1);
return true;
}
Collecting nulls is sad. Can we just filter them out and then check the size of the result?
Question 8 bad code: Normalization
public boolean fix(Product product){
Stream
data = Stream.concat(data, Stream.of(product.description)); List
.filter(this::polite) .map(this::fixGrammar) .collect(Collectors.toList());
if(reviews.size()!=product.reviews.size()+1){ return false; } product.description = reviews.get(reviews.size()-1); product.reviews = reviews.sublist(0, reviews.size()-1); return true;
}
Finally, we can format this code up to a consistent personal taste.
Question 8 bad code: Normalization
//version obtained by normalization
Stream
data = Stream.concat(data, Stream.of(product.description)); List
.filter(this::polite) .map(this::fixGrammar) .collect(Collectors.toList());
if(reviews.size()!=product.reviews.size()+1){ return false; } product.description = reviews.get(reviews.size()-1); product.reviews = reviews.sublist(0, reviews.size()-1); return true;
//model solution
Stream
var msgs=Stream.concat(des,product.reviews.stream()).parallel()
.filter(this::polite) .map(this::fixGrammar) .collect(Collectors.toList());
if(msgs.size()!=product.reviews.size()+1){return false;} product.description=msgs.get(0); product.reviews=msgs.subList(1,msgs.size());
return true;
Oh, look, the good version of the code was just hidden behind the bad version!
Question 3
Question 3a: [10]
Explain why so many methods in Java throws InterruptedException?
What is the purpose of this exception?
Question 3 Consider the following code:
public String guessPassword(){
while(true){
String result = guessOneTime();
if(result!=null){ return result; }
}
}
Doing minimal code modifications, turn guessPassword into an interruptible method.
public String guessPassword()throws InterruptedException{ while(true){
if(Thread.interrupted()){ throw new InterruptedException(); } String result = guessOneTime();
if(result!=null){ return result; }
}
}
Question 3b: [10]
● ●
●
●
Next time
More of an HelpDesk:
Prepare some questions and I will answer in the lecture.
There will be no slides, so if no one have questions I will just look at you silently for 50 mins 🙂
So, review the slides and prepare questions ! There must be a lot that I can clarify!