threads n.
Download
Skip this Video
Loading SlideShow in 5 Seconds..
נושאים מתקדמים ב Threads PowerPoint Presentation
Download Presentation
נושאים מתקדמים ב Threads

Loading in 2 Seconds...

play fullscreen
1 / 21

נושאים מתקדמים ב Threads - PowerPoint PPT Presentation


  • 186 Views
  • Uploaded on

נושאים מתקדמים ב Threads. תכנות מתקדם 89-210 תרגול מספר 6 תשע"א 2010-2011. אליהו חלסצ'י. בשיעור הקודם למדנו על אופן פעולת ה thrads ב java , ואיך להריץ אותם ברמה בסיסית

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

נושאים מתקדמים ב Threads


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
threads

נושאים מתקדמים ב Threads

תכנות מתקדם 89-210תרגול מספר 6

תשע"א 2010-2011

אליהו חלסצ'י

slide2
בשיעור הקודם למדנו על אופן פעולת ה thrads ב java, ואיך להריץ אותם ברמה בסיסית

השיטות בהן השתמשנו מתאימות למשימות פשוטות, אך למשימות מורכבות יותר נדרש Higher-Level API, כזה שיסתיר לנו את הלוגיקה מאחורי ניהול ה threads, ויוכל לבצע משימות מתקדמות יותר

הוא נחוץ אף יותר עבור אפליקציות כבדות שמנצלות באופן מלא את המערכות מרובות המעבדים \ ליבות המצויות כיום

בגרסא 5, נכנס java.util.concurrent עליו נלמד היום

הקדמה
slide3
תזמון משימות

publicclass ThreadTest {

privatestaticclass Ping implements Runnable{

publicvoid run(){while(true)System.out.println("ping");}

}

privatestaticclass Pong implements Runnable{

publicvoid run(){while(true)System.out.println("pong");}

}

publicstaticvoid 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();

}

}

מחלקה אחת תמיד כותבתping והשנייה תמיד pongאנו רוצים להתחיל מ pingושהשניים יתחלפו בכל חצישנייה.

כרגע הם נכתבים לא לפיהסדר ולא לא לפי הקצב

slide4

תזמון משימות

דוגמא עם wait()

publicclass Player implements Runnable{

private String hit;// ping or pong

Object lock;

public Player(String hit,Object o) {

this.hit=hit;

lock=o;

}

@Override

publicvoid run() {

synchronized (lock) {

while(true){

System.out.println(hit);

lock.notify();

try {

lock.wait();

}catch (InterruptedException e) {}

}}}}

Object obj=new Object();

Thread t1=new Thread(new Player("ping",obj));

Thread t2=new Thread(new Player("pong",obj));

t1.start();

t2.start();

Causer Output t1 t2

t1  ping wait notify

t2  pong notify wait

t1  ping wait notify

t2  pong notify wait

t1  ping wait notify

slide5
תזמון משימות

ניתן גם לפתור את הבעיה

באמצעות sleep למשך

שנייה, ולהתחיל את ה

Threads בהפרש של

חצי שנייה

הפיתרון כמובן מסורבל

publicclass ThreadTest {

privatestaticclass Ping implements Runnable{

publicvoid run(){

while(true){

System.out.println("ping");

try {Thread.sleep(1000);}

catch (InterruptedException e) {}

}

}

}

privatestaticclass Pong implements Runnable{

publicvoid run(){

while(true){

System.out.println("pong");

try {Thread.sleep(1000);}

catch (InterruptedException e) {}

}

}

}

publicstaticvoid main(String[] args) throws InterruptedException {

Ping ping=new Ping();

Pong pong=new Pong();

Thread t=new Thread(ping,"thread 1");

Thread t1=new Thread(pong,"thread 2");

t.start();

long time2,time=System.currentTimeMillis();

while((time2=System.currentTimeMillis())-time<500);

t1.start();

}

}

slide6
תזמון משימות

המחלקה Timer יכולה לבצע את כל

ההתאמות הדרושות מאחורי הקלעים

ולתזמן משימות ב thread שרץ ברקע

ניתן לבטל משימות ע"י קריאה לcancel() אך ה timer ימשיך לרוץ

ניתן לבטל את ה timer עצמו ע"יקריאה ל cancel() שלו

import java.util.Timer;

import java.util.TimerTask;

publicclass ThreadTest {

privatestaticclass Ping extends TimerTask{

publicvoid run(){System.out.println("ping");}

}

privatestaticclass Pong extends TimerTask{

publicvoid run(){System.out.println("pong");}

}

publicstaticvoid 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);

}

}

int i;

while((i=System.in.read())!=13);

ping.cancel(); בוטל ping ממשיך t

pong.cancel(); בוטל pong ממשיך t

t.cancel();בוטל t

slide7
דוגמת סנכרון משיעור קודם

publicclass ThreadTest {

privatestaticclass CountAdapter implements Runnable{

Count c;

public CountAdapter(Count c){

this.c=c;

}

publicvoid run(){

for(int i=0;i<100000000;i++)

c.update();

}

}

publicstaticvoid main(String[] args) {

Count c=new Count();

c.setCount(0);

CountAdapter ca=new CountAdapter(c);

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);

}}

publicclass Count {

privateintcount;

publicvoid setCount(int x){count=x;}

publicint getCount(){returncount;}

publicsynchronized void update(){count++;}

}

שיעור קודם שחקנו עם מיקום הסנכרוןוהגענו לפשרה בין מקביליות למהירות

בדוגמא אפשרנו מקביליות על חשבוןמהירות ריצה...

זמן הריצה היה כ 46 שניות

כעת נשתמש במשתנה אטומי

slide8
משתנים אטומיים

publicclass ThreadTest {

privatestaticclass CountAdapter implements Runnable{

Count c;

public CountAdapter(Count c){

this.c=c;

}

publicvoid run(){

for(int i=0;i<100000000;i++)

c.update();

}

}

publicstaticvoid main(String[] args) {

Count c=new Count();

c.setCount(0);

CountAdapter ca=new CountAdapter(c);

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);

}}

import java.util.concurrent.atomic.AtomicInteger;

publicclass Count {

private AtomicInteger count=new AtomicInteger(0);

publicvoid setCount(int x){count.set(x);}

publicint getCount(){returncount.get();}

publicvoid update(){

count.incrementAndGet();// count++

}

}

הפעם השתמשנו באובייקטAtomicInteger – רק thread אחד

יכול לבצע עליו פעולות.

זמן הריצה היה כ 6 שניות.

deadlock
דוגמא ל deadlock

publicclass Friend {

private String name;

public Friend(String name){this.name = name;}

public String getName(){returnthis.name;}

publicsynchronizedvoid sayHA(Friend date) {

System.out.format("%s says HA to %s%n",this.name, date.getName());

date.sayDA(this);

}

publicsynchronizedvoid sayDA(Friend date) {

System.out.format("%s says DA to %s%n",this.name,date.getName());

}

}

  • נתון הפרוטוקול הבא לפתיחת "דייט": אם אחד המשתתפים אומר "הא" האחר צריך להשיב "דה"
  • בדוגמא, Alice ו Bob החיים ב threads
  • נפרדים, יצאו ל"דייט"
  • שניהם מתפרצים ואומרים "הא"
  • בגלל הסנכרון על אמירת ה"הא", האובייקטים של Alice ושל Bob נעולים עד שאמירת ה"הא" תסתיים
  • אמירת ה"הא" לא מסתיימת עד שהאחר יאמר "דה"
  • אבל יש גם סנכרון על אמירת ה"דה", ואם האובייקט נעול, ה Thread השני לא יכול להיכנס אליו
  • Alice ו Bob תקועים בשתיקה מביכה

publicclass DeadLock {

privatestaticclass FriendRun implements Runnable{

private Friend me,date;

public FriendRun(Friend m,Friend d){ me=m;date=d;}

publicvoid run(){ me.sayHA(date); }

}

publicstaticvoid 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();

t1.start();

}}

Alice says HA to Bob

Bob says HA to Alice

deadlock1

t1

FR

FR

A

B

B

A

Start()

t2

Start()

דוגמא ל deadlock

publicclass Friend {

private String name;

public Friend(String name){this.name = name;}

public String getName(){returnthis.name;}

publicsynchronizedvoid sayHA(Friend date) {

System.out.format("%s says HA to %s%n",this.name, date.getName());

date.sayDA(this);

}

publicsynchronizedvoid sayDA(Friend date) {

System.out.format("%s says DA to %s%n",this.name,date.getName());

}

}

A

B

A.sayHA()

A נעול

publicclass DeadLock {

privatestaticclass FriendRun implements Runnable{

private Friend me,date;

public FriendRun(Friend m,Friend d){ me=m;date=d;}

publicvoid run(){ me.sayHA(date); }

}

publicstaticvoid 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();

t1.start();

}}

B.sayHA()

B נעול

t1: B.sayDA()

א"א B נעול

לא יוצאים מ A.sayHa()

A נשאר נעול...

t2: A.sayDA()

א"א A נעול

לא יוצאים מ B.sayHa()

B נשאר נעול...

אף צד לא סיים לומר HA

slide11
נעילת אובייקטים ע"י lock

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;

publicclass Friend {

private String name;

private Lock lock = new ReentrantLock();

public Friend(String name){this.name = name;}

public String getName(){returnthis.name;}

publicboolean iCanLockBoth(Friend date) {

Boolean myLock = false;

Boolean yourLock = false;

try {

myLock=lock.tryLock();

yourLock=date.lock.tryLock();

} finally {

if (!(myLock&&yourLock)) {

if (myLock) lock.unlock();

if (yourLock) date.lock.unlock();

}

}

return myLock&&yourLock;

}

publicvoid sayHA(Friend date) {

if(iCanLockBoth(date)){

try{

System.out.format("%s says HA to %s%n",this.name,date.getName());

date.sayDA(this);

} finally{

lock.unlock();

date.lock.unlock();

}

} else

System.out.format("%s started to say HA but realized %s already started",name,date.getName());

}

iCanLockBoth יחסיר אמת רק אם הצלחנו לנעול את 2המנעולים (שלי ושל ה "דייט") כלומר 2 האובייקטים נעולים ע"י

אותו ה thread וכעת אין בעיה שאמירת ה"הא" תצפה ל"דה"

Alice says HA to Bob

Bob says DA to Alice

Bob started to say HA but realized Alice already started

executor
בדוגמאות שראינו עד עתה היה קשר קרוב בין המשימה שצריכה להתבצע לבין ה thread שמריץ אותה

באפליקציות קטנות זה בסדר, אך באפליקציות גדולות הגיוני יותר להפריד את יצירת וניהול ה threads משאר האפליקציה

ממשק ה Executor משמש אותנו לכך

הממשק Executor
executor1
לממשק יש מתודה אחת בשם:(public void execute(Runnable r

כאשר השליטה איך להריץ את r.run() בידנו

דוגמאות:

הרצה ישירה:

הרצה ב thread נפרדלכל משימה:

הממשק Executor

class DirectExecutor implements Executor {

publicvoid execute(Runnable r) {

r.run();

}

}

class ThreadPerTaskExecutor implements Executor {

publicvoid execute(Runnable r) {

new Thread(r).start();

}

}

executor2
נניח שאנו נרצה לשלוט בכמות ה threads שרצים במקביל, כדי לשלוט בעומס על המערכת.

נוכל לממש תור שיאפשר רק ל N מסוים של threads לרוץ מתוכו. שיטה זו נקראת ThreadPool

אופן המימוש:

במתודה execute פשוט נכניס את r לתור.

נממש מתודה בשם run ששולפת NRunnables מהתור, ומריצה אותם ב threads משלהם

לא נשלוף אחרים כל עוד אלו לא הסתיימו

אבל נצטרך לטפל במקרים שונים של sleep, wait וכו' שזה כבר יותר קשה לממש בצורה טובה

לכן ב java.util.concurrent כבר ממשו ThreadPools חזקים וגמישים

הממשק Executor
threadpools
ThreadPools

import java.util.concurrent.Executor;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

publicclass ThreadTest3 {

privatestaticvoid delay(long ms){

try { Thread.sleep(ms);}

catch (InterruptedException e) {}

}

privatestaticclass RunnableTask1 implements Runnable{

publicvoid run(){

System.out.println("task1 started");

delay(10000);

System.out.println("task1 finished");

}

}…

publicstaticvoid main(String[] args) {

Executor executor = Executors.newSingleThreadExecutor();

executor.execute (new RunnableTask1 ());

executor.execute (new RunnableTask2 ());

executor.execute (new RunnableTask3 ());

((ExecutorService) executor).shutdown();

}

אנו קוראים בצורה סטטית ל factory שיוצר

ExectuterService, כאשר במקרה שלנוnewSingleTreadExecutor() מאפשר רקל thread אחד לרוץ בכל פעם.

איך יראה הפלט?

ובהתאמה המחלקות RunnableTask2

ו RunnableTask 3

task1 started

task1 finished

task2 started

task2 finished

task3 started

task3 finished

threadpools1
ThreadPools

import java.util.concurrent.Executor;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

publicclass ThreadTest3 {

privatestaticvoid delay(long ms){

try { Thread.sleep(ms);}

catch (InterruptedException e) {}

}

privatestaticclass RunnableTask1 implements Runnable{

publicvoid run(){

System.out.println("task1 started");

delay(10000);

System.out.println("task1 finished");

}

}…

publicstaticvoid main(String[] args) {

Executor executor = Executors.newFixedThreadPool (2);

executor.execute (new RunnableTask1 ());

executor.execute (new RunnableTask2 ());

executor.execute (new RunnableTask3 ());

((ExecutorService) executor).shutdown();

}

הפעם Executors.newFixedThreadPoolמאפשר רק למספר מסוים של threads לרוץ.

איך יראה הפלט?

ובהתאמה המחלקות RunnableTask2

ו RunnableTask 3

task1 started

task2 started

task1 finished

task2 finished

task3 started

task3 finished

threadpools2
ThreadPools

import java.util.concurrent.Executor;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

publicclass ThreadTest3 {

privatestaticvoid delay(long ms){

try { Thread.sleep(ms);}

catch (InterruptedException e) {}

}

privatestaticclass RunnableTask1 implements Runnable{

publicvoid run(){

System.out.println("task1 started");

delay(10000);

System.out.println("task1 finished");

}

}…

publicstaticvoid main(String[] args) {

Executor executor = Executors.newCachedThreadPool();

executor.execute (new RunnableTask1 ());

executor.execute (new RunnableTask2 ());

executor.execute (new RunnableTask3 ());

((ExecutorService) executor).shutdown();

}

ל Executors.newCachedThreadPool

אין הגבלה על מספר ה threads

איך יראה הפלט?

ובהתאמה המחלקות RunnableTask2

ו RunnableTask 3

task1 started

task2 started

task3 started

task1 finished

task3 finished

task2 finished

callable future
Callable, Future

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Future;

publicclass ThreadTest3 {

privatestaticvoid delay(long ms){

try { Thread.sleep(ms);}

catch (InterruptedException e) {}

}

privatestaticclass CallableTask implements Callable<Integer>{

Integer i;

public CallableTask(int i){

this.i=new Integer(i);

}

public Integer call() throws Exception {

System.out.printf("task%d started\n",i);

delay(10000);

returni;

}

}

publicstaticvoid main(String[] args) throws InterruptedException, ExecutionException {

ExecutorService executor = Executors.newCachedThreadPool();

Future<Integer> futures[]=new Future[3];

for(int i=0;i<3;i++)

futures[i]=executor.submit (new CallableTask(i+1));

executor.shutdown();

for(int i=0;i<3;i++)

System.out.printf("task%d finished\n",futures[i].get());

}

ExecutorService יודע לעבוד גם עם

Callable, שבדומה ל Runnable גם לו

מתודה אחת call, אך היא יכולה להחזיר

ערך ואף לזרוק exception כרצוננו, וכך

קל יותר להעביר מידע.

המתודה submit תחזיר את הערך שלcall לתוך אובייקט מסוג Future.

ל Future מתודות שימושיות נוספות.

task1 started

task2 started

task3 started

task1 finished

task3 finished

task2 finished

slide19
רוב מבני הנתונים של java.util אינם מסונכרנים

ניתן לעטוף אותם בטיפוסים מסונכרנים לדוגמא:

כך רק מי שצריך, משלם אתמחיר זמן הריצה...

בעוד שפעולות בודדות הן thread-safe, רצף של פעולות התלויות כל אחת בתוצאת קודמתה, אינה thread-safe

טעות נפוצה היא לחשוב ששיטה זו thread-safe לגמריי

בגרסא 5, נוספו מבני הנתונים ב java.util.concurrent

מבני נתונים אלו הם thread-safe לחלוטין, ואף מהירים בהרבה ממבנה רגיל העטוף ב synchronized

מבני נתונים:

ArrayBlockingQueue<E>

ConcurrentHashMap<K,V>

ConcurrentLinkedQueue<E>

מבני נתונים מסונכרנים

private Map<String,Integer> hm = Collections.synchronizedMap( new HashMap<String,Integer>());

slide20
האם תמיד מובטח שהמחשב ימליץ ל Bob לצאת עם Alice? (הקוד בשקף הבא)

הראו תוכנית שתבטיח זאת

כתבו תוכנית שמעדכנת HashMap בכמה threads ברמה שחוסר הסנכרון יפגע בתוצאות

עטפו את המחלקה בסנכרון, ומדדו את הזמן.

החליפו את מבנה הנתונים ל ConcurrentHashMap ומדדו את הזמן שוב

הטמעה
slide21

publicclass ThreadTest4 {

static String message;

privatestaticclass CorrectorThread extends Thread {

publicvoid run() {

try {sleep(1000);}

catch (InterruptedException e) {}

message = "Bob, ask Alice on a date.";

}

}

publicstaticvoid main(String args[]) throws InterruptedException {

(new CorrectorThread()).start();

message = "Bob, don't ask Alice on a date.";

Thread.sleep(2000);

System.out.println(message);

}

}