נושאים מתקדמים ב Threads
Download
Report
Transcript נושאים מתקדמים ב Threads
הקדמה
תזמון משימות
משתנים אטומיים
מנעולים
Executor
ThreadPools
מבני נתונים מסונכרנים
הטמעה
89-210תכנות מתקדם -תרגול 6
תכנות מתקדם 89-210
תרגול מספר 6
תשע"א 2010-2011
נושאים מתקדמים ב Threads
אליהו חלסצ'י
89-210תכנות מתקדם -תרגול 6
הקדמה
•
•
•
•
הקדמה
תזמון משימות
משתנים אטומיים
מנעולים
Executor
ThreadPools
מבני נתונים מסונכרנים
הטמעה
בשיעור הקודם למדנו על אופן פעולת ה thradsב ,java
ואיך להריץ אותם ברמה בסיסית
השיטות בהן השתמשנו מתאימות למשימות פשוטות ,אך
למשימות מורכבות יותר נדרש ,Higher-Level APIכזה
שיסתיר לנו את הלוגיקה מאחורי ניהול ה ,threadsויוכל
לבצע משימות מתקדמות יותר
הוא נחוץ אף יותר עבור אפליקציות כבדות שמנצלות
באופן מלא את המערכות מרובות המעבדים \ ליבות
המצויות כיום
בגרסא ,5נכנס java.util.concurrentעליו נלמד היום
הקדמה
תזמון משימות
משתנים אטומיים
מנעולים
Executor
ThreadPools
מבני נתונים מסונכרנים
הטמעה
6 תרגול- תכנות מתקדם89-210
תזמון משימות
public class ThreadTest {
private static class Ping implements Runnable{
public void run(){while(true)System.out.println("ping");}
}
private static class Pong implements Runnable{
public void run(){while(true)System.out.println("pong");}
}
public static void main(String[] args) {
Ping ping=new Ping();
Pong pong=new Pong();
Thread t=new Thread(ping,"thread 1");
Thread t1=new Thread(pong,"thread 2");
t.start();
t1.start();
}
}
מחלקה אחת תמיד כותבת
pong והשנייה תמידping
ping אנו רוצים להתחיל מ
ושהשניים יתחלפו בכל חצי
.שנייה
כרגע הם נכתבים לא לפי
הסדר ולא לא לפי הקצב
הקדמה
תזמון משימות
משתנים אטומיים
מנעולים
Executor
ThreadPools
מבני נתונים מסונכרנים
הטמעה
6 תרגול- תכנות מתקדם89-210
תזמון משימות
wait() דוגמא עם
public class Player implements Runnable{
private String hit;// ping or pong
Object lock;
Object obj=new Object();
public Player(String hit,Object o) {
Thread t1=new Thread(new Player("ping",obj));
this.hit=hit;
Thread t2=new Thread(new Player("pong",obj));
lock=o;
t1.start();
}
t2.start();
@Override
public void run() {
synchronized (lock) {
while(true){
System.out.println(hit);
lock.notify();
try {
lock.wait();
}catch (InterruptedException e) {}
}}}}
Causer Output
t1
t2
t1
t2
t1
t2
t1
…
wait
notify
wait
notify
wait
notify
wait
notify
wait
notify
ping
pong
ping
pong
ping
הקדמה
תזמון משימות
משתנים אטומיים
מנעולים
Executor
ThreadPools
מבני נתונים מסונכרנים
הטמעה
6 תרגול- תכנות מתקדם89-210
תזמון משימות
public class ThreadTest {
private static class Ping implements Runnable{
public void run(){
while(true){
System.out.println("ping");
try {Thread.sleep(1000);}
ניתן גם לפתור את הבעיה
למשךsleep באמצעות
ולהתחיל את ה,שנייה
בהפרש שלThreads
חצי שנייה
הפיתרון כמובן מסורבל
catch (InterruptedException e) {}
}
public static void main(String[] args) throws
InterruptedException {
}
Ping ping=new Ping();
}
Pong pong=new Pong();
private static class Pong implements Runnable{
Thread t=new Thread(ping,"thread 1");
public void run(){
Thread t1=new Thread(pong,"thread 2");
while(true){
System.out.println("pong");
t.start();
try {Thread.sleep(1000);}
long time2,time=System.currentTimeMillis();
catch (InterruptedException e) {}
while((time2=System.currentTimeMillis())-time<500);
t1.start();
}
}
}
}
}
הקדמה
תזמון משימות
משתנים אטומיים
מנעולים
Executor
ThreadPools
מבני נתונים מסונכרנים
הטמעה
6 תרגול- תכנות מתקדם89-210
תזמון משימות
import java.util.Timer;
import java.util.TimerTask;
public class ThreadTest {
private static class Ping extends TimerTask{
public void run(){System.out.println("ping");}
}
private static class Pong extends TimerTask{
public void run(){System.out.println("pong");}
}
public static void main(String[] args){
Ping ping=new Ping();
Pong pong=new Pong();
Timer t=new Timer();
t.scheduleAtFixedRate(ping, 0, 1000);
t.scheduleAtFixedRate(pong, 500, 1000);
}
}
יכולה לבצע את כלTimer המחלקה
ההתאמות הדרושות מאחורי הקלעים
שרץ ברקעthread ולתזמן משימות ב
ניתן לבטל משימות ע"י קריאה ל
ימשיך לרוץtimer אך הcancel()
עצמו ע"יtimer ניתן לבטל את ה
שלוcancel() קריאה ל
int i;
while((i=System.in.read())!=13);
ping.cancel(); בוטלping ממשיךt
pong.cancel(); בוטלpong ממשיךt
t.cancel(); בוטלt
הקדמה
תזמון משימות
משתנים אטומיים
מנעולים
Executor
ThreadPools
מבני נתונים מסונכרנים
הטמעה
6 תרגול- תכנות מתקדם89-210
דוגמת סנכרון משיעור קודם
public class ThreadTest {
private static class CountAdapter implements Runnable{
public class Count {
Count c;
private int count;
public CountAdapter(Count c){
public void setCount(int x){count=x;}
public int getCount(){return count;}
this.c=c;
}
public void run(){
for(int i=0;i<100000000;i++)
c.update();
public synchronized void
update(){count++;}
}
}
}
שיעור קודם שחקנו עם מיקום הסנכרון
והגענו לפשרה בין מקביליות למהירות
public static void main(String[] args) {
Count c=new Count();
c.setCount(0);
CountAdapter ca=new CountAdapter(c);
בדוגמא אפשרנו מקביליות על חשבון
...מהירות ריצה
שניות46 זמן הריצה היה כ
Thread t=new Thread(ca);
Thread t1=new Thread(ca);
long time=System.currentTimeMillis();
t.start();
t1.start();
כעת נשתמש במשתנה אטומי
while(t.isAlive() || t1.isAlive());
System.out.println(c.getCount());
System.out.println((System.currentTimeMillis()-time)/1000);
}}
הקדמה
תזמון משימות
משתנים אטומיים
מנעולים
Executor
ThreadPools
מבני נתונים מסונכרנים
הטמעה
6 תרגול- תכנות מתקדם89-210
משתנים אטומיים
public class ThreadTest {
private static class CountAdapter implements Runnable{
import java.util.concurrent.atomic.AtomicInteger;
Count c;
public CountAdapter(Count c){
public class Count {
this.c=c;
private AtomicInteger count=new AtomicInteger(0);
}
public
public void run(){
void setCount(int x){count.set(x);}
public int getCount(){return count.get();}
for(int i=0;i<100000000;i++)
c.update();
public void update(){
}
count.incrementAndGet();// count++
}
}
public static void main(String[] args) {
}
Count c=new Count();
c.setCount(0);
הפעם השתמשנו באובייקט
אחדthread – רקAtomicInteger
.יכול לבצע עליו פעולות
CountAdapter ca=new CountAdapter(c);
Thread t=new Thread(ca);
Thread t1=new Thread(ca);
long time=System.currentTimeMillis();
t.start();
. שניות6 זמן הריצה היה כ
t1.start();
while(t.isAlive() || t1.isAlive());
System.out.println(c.getCount());
System.out.println((System.currentTimeMillis()-time)/1000);
}}
הקדמה
תזמון משימות
משתנים אטומיים
מנעולים
Executor
ThreadPools
מבני נתונים מסונכרנים
הטמעה
6 תרגול- תכנות מתקדם89-210
deadlock דוגמא ל
public class Friend {
נתון הפרוטוקול הבא
אם אחד המשתתפים אומר:"לפתיחת "דייט
""הא" האחר צריך להשיב "דה
threads החיים בBob וAlice ,בדוגמא
" יצאו ל"דייט,נפרדים
"• שניהם מתפרצים ואומרים "הא
האובייקטים,"• בגלל הסנכרון על אמירת ה"הא
" נעולים עד שאמירת ה"האBob ושלAlice של
תסתיים
• אמירת ה"הא" לא מסתיימת עד שהאחר
"יאמר "דה
ואם,"• אבל יש גם סנכרון על אמירת ה"דה
השני לא יכולThread ה,האובייקט נעול
להיכנס אליו
תקועים בשתיקה מביכהBob וAlice •
private String name;
public Friend(String name){this.name = name;}
public String getName(){return this.name;}
public synchronized void sayHA(Friend date) {
System.out.format("%s says HA to %s%n",this.name, date.getName());
date.sayDA(this);
}
public synchronized void sayDA(Friend date) {
System.out.format("%s says DA to %s%n",this.name,date.getName());
}
}
public class DeadLock {
private static class FriendRun implements Runnable{
private Friend me,date;
public FriendRun(Friend m,Friend d){ me=m;date=d;}
public void run(){ me.sayHA(date); }
}
public static void main(String[] args) {
Friend alice = new Friend("Alice");
Friend bob = new Friend("Bob");
Alice says HA to Bob
Thread t=new Thread(new FriendRun(alice,bob));
Bob says HA to Alice
Thread t1=new Thread(new FriendRun(bob,alice));
t.start();
t1.start();
}}
הקדמה
תזמון משימות
משתנים אטומיים
מנעולים
Executor
ThreadPools
מבני נתונים מסונכרנים
הטמעה
6 תרגול- תכנות מתקדם89-210
deadlock דוגמא ל
public class Friend {
A
private String name;
public Friend(String name){this.name = name;}
B
public String getName(){return this.name;}
public synchronized void sayHA(Friend date) {
t1 FR
A
System.out.format("%s says HA to %s%n",this.name, date.getName());
B
date.sayDA(this);
}
public synchronized void sayDA(Friend date) {
Start()
t2 FR
B
A
System.out.format("%s says DA to %s%n",this.name,date.getName());
}
}
A.sayHA()
נעולA
t1: B.sayDA()
נעולB א"א
לא יוצאים מ
A.sayHa()
... נשאר נעולA
Start()
public class DeadLock {
private static class FriendRun implements Runnable{
B.sayHA()
נעולB
private Friend me,date;
public FriendRun(Friend m,Friend d){ me=m;date=d;}
public void run(){ me.sayHA(date); }
t2: A.sayDA()
נעולA א"א
לא יוצאים מ
B.sayHa()
... נשאר נעולB
}
public static void main(String[] args) {
Friend alice = new Friend("Alice");
Friend bob = new Friend("Bob");
Thread t=new Thread(new FriendRun(alice,bob));
Thread t1=new Thread(new FriendRun(bob,alice));
t.start();
HA אף צד לא סיים לומר
t1.start();
}}
הקדמה
תזמון משימות
משתנים אטומיים
מנעולים
Executor
ThreadPools
מבני נתונים מסונכרנים
הטמעה
6 תרגול- תכנות מתקדם89-210
lock נעילת אובייקטים ע"י
import java.util.concurrent.locks.Lock;
public void sayHA(Friend date) {
import java.util.concurrent.locks.ReentrantLock;
if(iCanLockBoth(date)){
public class Friend {
try{
private String name;
System.out.format("%s says HA to %s%n",this.name,date.getName());
date.sayDA(this);
} finally{
private Lock lock = new ReentrantLock();
lock.unlock();
date.lock.unlock();
public Friend(String name){this.name = name;}
}
public String getName(){return this.name;}
} else
public boolean iCanLockBoth(Friend date) {
Boolean myLock = false;
Boolean yourLock = false;
try {
myLock=lock.tryLock();
yourLock=date.lock.tryLock();
} finally {
System.out.format("%s started to say HA but realized %s already
started",name,date.getName());
}
2 יחסיר אמת רק אם הצלחנו לנעול אתiCanLockBoth
האובייקטים נעולים ע"י2 המנעולים (שלי ושל ה "דייט") כלומר
" וכעת אין בעיה שאמירת ה"הא" תצפה ל"דהthread אותו ה
if (!(myLock&&yourLock)) {
if (myLock) lock.unlock();
if (yourLock) date.lock.unlock();
}
}
return myLock&&yourLock;
}
Alice says HA to Bob
Bob says DA to Alice
Bob started to say HA but realized Alice already started
89-210תכנות מתקדם -תרגול 6
הממשק Executor
הקדמה
תזמון משימות
משתנים אטומיים
מנעולים
Executor
ThreadPools
מבני נתונים מסונכרנים
הטמעה
• בדוגמאות שראינו עד עתה היה קשר קרוב בין
המשימה שצריכה להתבצע לבין ה threadשמריץ
אותה
• באפליקציות קטנות זה בסדר ,אך באפליקציות
גדולות הגיוני יותר להפריד את יצירת וניהול ה
threadsמשאר האפליקציה
• ממשק ה Executorמשמש אותנו לכך
הקדמה
תזמון משימות
משתנים אטומיים
מנעולים
Executor
ThreadPools
מבני נתונים מסונכרנים
הטמעה
6 תרגול- תכנות מתקדם89-210
Executor הממשק
:• לממשק יש מתודה אחת בשם
public void execute(Runnable r(
בידנוr.run() • כאשר השליטה איך להריץ את
:• דוגמאות
class DirectExecutor implements Executor {
public void execute(Runnable r) {
r.run();
}
:– הרצה ישירה
}
class ThreadPerTaskExecutor implements Executor {
public void execute(Runnable r) {
new Thread(r).start();
}
}
נפרדthread – הרצה ב
:לכל משימה
89-210תכנות מתקדם -תרגול 6
הממשק Executor
הקדמה
תזמון משימות
משתנים אטומיים
מנעולים
Executor
ThreadPools
מבני נתונים מסונכרנים
הטמעה
• נניח שאנו נרצה לשלוט בכמות ה threadsשרצים במקביל,
כדי לשלוט בעומס על המערכת.
• נוכל לממש תור שיאפשר רק ל Nמסוים של threadsלרוץ
מתוכו .שיטה זו נקראת ThreadPool
• אופן המימוש:
– במתודה executeפשוט נכניס את rלתור.
– נממש מתודה בשם runששולפת Runnables Nמהתור ,ומריצה
אותם ב threadsמשלהם
– לא נשלוף אחרים כל עוד אלו לא הסתיימו
• אבל נצטרך לטפל במקרים שונים של wait ,sleepוכו' שזה
כבר יותר קשה לממש בצורה טובה
• לכן ב java.util.concurrentכבר ממשו ThreadPools
חזקים וגמישים
הקדמה
תזמון משימות
משתנים אטומיים
מנעולים
Executor
ThreadPools
מבני נתונים מסונכרנים
הטמעה
6 תרגול- תכנות מתקדם89-210
ThreadPools
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
public static void main(String[] args) {
import java.util.concurrent.Executors;
Executor executor = Executors.newSingleThreadExecutor();
public class ThreadTest3 {
executor.execute (new RunnableTask1 ());
executor.execute (new RunnableTask2 ());
private static void delay(long ms){
try { Thread.sleep(ms);}
executor.execute (new RunnableTask3 ());
catch (InterruptedException e) {}
((ExecutorService) executor).shutdown();
}
}
private static class RunnableTask1 implements Runnable{
public void run(){
System.out.println("task1 started");
delay(10000);
System.out.println("task1 finished");
}
}…
RunnableTask2 ובהתאמה המחלקות
RunnableTask 3 ו
שיוצרfactory אנו קוראים בצורה סטטית ל
כאשר במקרה שלנו,ExectuterService
מאפשר רקnewSingleTreadExecutor()
. אחד לרוץ בכל פעםthread ל
?איך יראה הפלט
task1 started
task1 finished
task2 started
task2 finished
task3 started
task3 finished
הקדמה
תזמון משימות
משתנים אטומיים
מנעולים
Executor
ThreadPools
מבני נתונים מסונכרנים
הטמעה
6 תרגול- תכנות מתקדם89-210
ThreadPools
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
public static void main(String[] args) {
import java.util.concurrent.Executors;
Executor executor = Executors.newFixedThreadPool (2);
public class ThreadTest3 {
executor.execute (new RunnableTask1 ());
executor.execute (new RunnableTask2 ());
private static void delay(long ms){
try { Thread.sleep(ms);}
executor.execute (new RunnableTask3 ());
catch (InterruptedException e) {}
((ExecutorService) executor).shutdown();
}
}
private static class RunnableTask1 implements Runnable{
public void run(){
System.out.println("task1 started");
delay(10000);
System.out.println("task1 finished");
}
}…
RunnableTask2 ובהתאמה המחלקות
RunnableTask 3 ו
Executors.newFixedThreadPool הפעם
. לרוץthreads מאפשר רק למספר מסוים של
?איך יראה הפלט
task1 started
task2 started
task1 finished
task2 finished
task3 started
task3 finished
הקדמה
תזמון משימות
משתנים אטומיים
מנעולים
Executor
ThreadPools
מבני נתונים מסונכרנים
הטמעה
6 תרגול- תכנות מתקדם89-210
ThreadPools
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
public static void main(String[] args) {
import java.util.concurrent.Executors;
Executor executor = Executors.newCachedThreadPool();
public class ThreadTest3 {
executor.execute (new RunnableTask1 ());
executor.execute (new RunnableTask2 ());
private static void delay(long ms){
try { Thread.sleep(ms);}
executor.execute (new RunnableTask3 ());
catch (InterruptedException e) {}
((ExecutorService) executor).shutdown();
}
}
private static class RunnableTask1 implements Runnable{
public void run(){
System.out.println("task1 started");
delay(10000);
System.out.println("task1 finished");
}
}…
RunnableTask2 ובהתאמה המחלקות
RunnableTask 3 ו
Executors.newCachedThreadPool ל
threads אין הגבלה על מספר ה
?איך יראה הפלט
task1 started
task2 started
task3 started
task1 finished
task3 finished
task2 finished
הקדמה
תזמון משימות
משתנים אטומיים
מנעולים
Executor
ThreadPools
מבני נתונים מסונכרנים
הטמעה
6 תרגול- תכנות מתקדם89-210
Callable, Future
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
public static void main(String[] args) throws
InterruptedException, ExecutionException {
import java.util.concurrent.ExecutorService;
ExecutorService executor = Executors.newCachedThreadPool();
import java.util.concurrent.Executors;
Future<Integer> futures[]=new Future[3];
import java.util.concurrent.Future;
for(int i=0;i<3;i++)
futures[i]=executor.submit (new CallableTask(i+1));
public class ThreadTest3 {
executor.shutdown();
private static void delay(long ms){
for(int i=0;i<3;i++)
try { Thread.sleep(ms);}
System.out.printf("task%d finished\n",futures[i].get());
catch (InterruptedException e) {}
}
}
private static class CallableTask implements Callable<Integer>{
Integer i;
public CallableTask(int i){
this.i=new Integer(i);
}
public Integer call() throws Exception {
}
task2 started
System.out.printf("task%d started\n",i);
task3 started
delay(10000);
task1 finished
return i;
}
task1 started
task3 finished
task2 finished
יודע לעבוד גם עםExecutorService
גם לוRunnable שבדומה ל,Callable
אך היא יכולה להחזיר,call מתודה אחת
וכך, כרצוננוexception ערך ואף לזרוק
.קל יותר להעביר מידע
תחזיר את הערך שלsubmit המתודה
.Future לתוך אובייקט מסוגcall
. מתודות שימושיות נוספותFuture ל
89-210תכנות מתקדם -תרגול 6
מבני נתונים מסונכרנים
•
•
•
•
•
•
•
•
הקדמה
תזמון משימות
משתנים אטומיים
מנעולים
Executor
ThreadPools
מבני נתונים מסונכרנים
הטמעה
רוב מבני הנתונים של java.utilאינם מסונכרנים
ניתן לעטוף אותם בטיפוסים מסונכרנים לדוגמא:
= private Map<String,Integer> hm
כך רק מי שצריך ,משלם את
Collections.synchronizedMap( new
מחיר זמן הריצה...
;))(>HashMap<String,Integer
בעוד שפעולות בודדות הן ,thread-safeרצף של פעולות התלויות
כל אחת בתוצאת קודמתה ,אינה thread-safe
טעות נפוצה היא לחשוב ששיטה זו thread-safeלגמריי
בגרסא ,5נוספו מבני הנתונים ב java.util.concurrent
מבני נתונים אלו הם thread-safeלחלוטין ,ואף מהירים בהרבה
ממבנה רגיל העטוף ב synchronized
מבני נתונים:
– >ArrayBlockingQueue<E
– >ConcurrentHashMap<K,V
– >ConcurrentLinkedQueue<E
89-210תכנות מתקדם -תרגול 6
הטמעה
•
•
•
•
•
הקדמה
תזמון משימות
משתנים אטומיים
מנעולים
Executor
ThreadPools
מבני נתונים מסונכרנים
הטמעה
האם תמיד מובטח שהמחשב ימליץ ל Bobלצאת
עם ( ?Aliceהקוד בשקף הבא)
הראו תוכנית שתבטיח זאת
כתבו תוכנית שמעדכנת HashMapבכמה
threadsברמה שחוסר הסנכרון יפגע בתוצאות
עטפו את המחלקה בסנכרון ,ומדדו את הזמן.
החליפו את מבנה הנתונים ל
ConcurrentHashMapומדדו את הזמן שוב
הקדמה
תזמון משימות
משתנים אטומיים
מנעולים
Executor
ThreadPools
מבני נתונים מסונכרנים
הטמעה
6 תרגול- תכנות מתקדם89-210
public class ThreadTest4 {
static String message;
private static class CorrectorThread extends Thread {
public void run() {
try {sleep(1000);}
catch (InterruptedException e) {}
message = "Bob, ask Alice on a date.";
}
}
public static void main(String args[]) throws InterruptedException {
(new CorrectorThread()).start();
message = "Bob, don't ask Alice on a date.";
Thread.sleep(2000);
System.out.println(message);
}
}