3. Основные ошибки
Deadlock
Одновременно едят соседи
Асимметричное распределение ресурсов
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 3 / 32
4. Устранение взамной блокировки
Разный порядок взятия вилок
Освобождение вилки в случае неудачной попытки взять обе вилки
Допуск к борьбе за вилки не всех философов
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 4 / 32
5. Решения
Философы берут вилки в разном порядке
Порядок для каждого философа зафиксирован
Порядок изменяется после еды
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 5 / 32
6. Решения
Пытаться взять вилки с помощью tryLock()
Случайная задержка в случае неудачи
Случайный порядок взятия вилок
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 6 / 32
7. Решения
Допуск к борьбе за вилки не всех философов
Глобальная блокировка - в каждый момент времени ест только
один философ
Очередь голодных философов
Фиксированное расписание обедов
Семафор - в каждый момент времени борьбу за вилки ведут N-1
философов
Официант, выдающий вилки философам
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 7 / 32
8. Решения
Синхронизация между соседями
Отсутствует глобальный объект синхронизации
Busy wait (check, sleep... check, sleep...)
Условная синхронизация (wait/notify)
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 8 / 32
9. Решения
Выравнивание количества обедов
Вилки не даются два раза подряд одному философу
Вежливые философы пропускают более голодных соседей (с
меньшим кол-вом обедов)
Допустимая разница в количестве обедов
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 9 / 32
10. Попытки взять вилки
1 public class Fork extends ReentrantLock {}
2 ...
3 boolean hungry = true ;
4 while ( hungry ) {
5 left . lock ();
6 try {
7 if ( right . tryLock ()) {
8 try {
9 eat ();
10 hungry = false ;
11 } finally {
12 right . unlock ();
13 }
14 }
15 } finally {
16 left . unlock ();
17 }
18 if ( hungry ) {
19 try {
20 Thread . sleep (10 + rnd . nextInt (10));
21 } catch ( I n t e r r u p t e d E x c e p t i o n e ) { break ; }
22 }
23 }
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 10 / 32
11. Условная синхронизация
1 think ();
2 synchronized ( TABLE ) {
3 state = " HUNGRY " ;
4 while (! canEat ()) {
5 try {
6 TABLE . wait ();
7 } catch ( I n t e r r u p t e d E x c e p t i o n e ) {
8 break ;
9 }
10 }
11 state = " EATING " ;
12 }
13 eat ();
14 synchronized ( TABLE ) {
15 state = " THINKING " ;
16 TABLE . notifyAll ();
17 }
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 11 / 32
12. Совесть
1 private boolean canEat () {
2 if ( LEFT . state . equals ( " EATING " ) ||
3 RIGHT . state . equals ( " EATING " )) {
4 return false ;
5 }
6 if ( LEFT . state . equals ( " HUNGRY " ) &&
7 eatCount - LEFT . eatCount > 10) {
8 return false ;
9 }
10 if ( RIGHT . state . equals ( " HUNGRY " ) &&
11 eatCount - RIGHT . eatCount > 10) {
12 return false ;
13 }
14 return true ;
15 }
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 12 / 32
13. Официант
1 think ();
2 philLock . lock ();
3 try {
4 waiter . makeOrder ( this );
5 while (! canEat )
6 condition . await ();
7 eat ();
8 canEat = false ;
9 waiter . thanks ( this );
10 } catch ( I n t e r r u p t e d E x c e p t i o n e ) {
11 break ;
12 } finally {
13 philLock . unlock ();
14 }
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 13 / 32
15. 1
Actors
Акторы - вычислительные сущности, взаимодействующие с
помощью асинхронной отправки сообщений
Внутреннее состояние актора скрыто от остальных акторов
Акторы могут выполняться независимо друг от друга
(одновременно)
Реализации
Языки Erlang, Io, Scala
Библиотека Akka (Java, Scala)
http://akka.io/
1
http://en.wikipedia.org/wiki/Actor_model
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 15 / 32
17. Fork
1 public class Fork extends UntypedActor {
2 ...
3 public void onReceive ( Object msg ) throws Exception {
4 ActorRef phil = getContext (). getSender (). get ();
5 CompletableFuture < Object > reply = null ;
6 if ( getContext (). getSenderFuture (). isDefined ()) {
7 reply = getContext (). getSenderFuture (). get ();
8 }
9 if ( msg instanceof String ) {
10 String msg_ = ( String ) msg ;
11 if ( msg_ . equals ( TAKE )) {
12 if (! used ) {
13 if ( owner == null ) {
14 owner = phil ;
15 } else if ( phil == owner ) {
16 reply . co m pl e te W it hR e su l t ( true );
17 } else {
18 if (! clean ) {
19 clean = true ;
20 owner = phil ;
21 reply . co m pl e te W it h Re su l t ( true );
22 } else {
23 reply . co m pl e te W it h Re su l t ( false );
24 }
25 }
26 } else {
27 if ( promise == null ) {
28 promise = phil ;
29 promiseReply = reply ;
30 } else {
31 reply . co m pl e te W it hR e su l t ( false );
32 }
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 17 / 32
18. Fork (2)
1 } else if ( msg_ . equals ( USE )) {
2 if ( phil == owner ) {
3 used = true ;
4 reply . co m pl et e Wi t hR e su l t ( true );
5 } else {
6 reply . co m pl et e Wi t hR e su l t ( false );
7 }
8 } else if ( msg_ . equals ( USED )) {
9 if ( phil == owner ) {
10 used = false ;
11 clean = false ;
12 if ( promise != null ) {
13 clean = true ;
14 owner = promise ;
15 promise = null ;
16 promiseReply . co m pl e te W it h Re s ul t ( true );
17 }
18 }
19 } else if ( msg_ . equals ( NOT_USED )) {
20 if ( phil == owner ) {
21 used = false ;
22 if ( promise != null ) {
23 clean = true ;
24 owner = promise ;
25 promise = null ;
26 promiseReply . co m pl e te W it h Re s ul t ( true );
27 }
28 }
29 } else throw new I l l e g a l A r g u m e n t E x c e p t i o n ( " Unknown message : " + msg );
30 } else throw new I l l e g a l A r g u m e n t E x c e p t i o n ( " Unknown message : " + msg );
31 }
32 }
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 18 / 32
19. Philosopher
1 public class Philosopher extends UntypedActor {
2 ...
3 public Philosopher ( int id , ActorRef left , ActorRef right ) {
4 ...
5 if ( id == 0) {
6 left . sendOneWay ( Fork . TAKE , me );
7 right . sendOneWay ( Fork . TAKE , me );
8 } else if ( id != size -1) {
9 left . sendOneWay ( Fork . TAKE , me );
10 }
11 }
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 19 / 32
20. Philosopher (2)
1 public void onReceive ( Object msg ) throws Exception {
2 if ( msg instanceof String ) {
3 String msg_ = ( String ) msg ;
4 if ( msg_ . equals ( THINK )) {
5 System . out . println ( " [ " + id + " ] Thinking " );
6 Scheduler . scheduleOnce ( me , START_EAT , 0 , TimeUnit . MILLISECONDS );
7 } else if ( msg_ . equals ( START_EAT )) {
8 if (( Boolean ) left . sendRequestReply ( Fork . TAKE , me )
9 && ( Boolean ) right . sendRequestReply ( Fork . TAKE , me )
10 && ( Boolean ) left . sendRequestReply ( Fork . USE , me )
11 && ( Boolean ) right . sendRequestReply ( Fork . USE , me )) {
12 System . out . println ( " [ " + id + " ] Eating " );
13 Scheduler . scheduleOnce ( me , FINISH_EAT , 0 , TimeUnit . MILLISECONDS );
14 } else {
15 left . sendOneWay ( Fork . NOT_USED , me );
16 Scheduler . scheduleOnce ( me , START_EAT , 0 , TimeUnit . MILLISECONDS );
17 }
18 } else if ( msg_ . equals ( FINISH_EAT )) {
19 left . sendOneWay ( Fork . USED , me );
20 right . sendOneWay ( Fork . USED , me );
21 Scheduler . scheduleOnce ( me , THINK , 0 , TimeUnit . MILLISECONDS );
22 } else throw new I l l e g a l A r g u m e n t E x c e p t i o n ( " [ " + id + " ] Unknown message : " + msg
23 } else throw new I l l e g a l A r g u m e n t E x c e p t i o n ( " [ " + id + " ] Unknown message : " + msg );
24 }
25 }
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 20 / 32
21. Table
1 public class Table {
2 ...
3 public Table ( int philCount ) {
4 forks = new ArrayList < ActorRef >( philCount );
5 for ( int i =0; i < philCount ; i ++) {
6 ActorRef fork = Actors . actorOf ( new ForkFactory ( i ));
7 forks . add ( fork );
8 fork . start ();
9 }
10 phils = new ArrayList < ActorRef >( philCount );
11 for ( int i =0; i < philCount ; i ++) {
12 ActorRef phil = Actors . actorOf (
13 new PhilFactory (i , ( i == philCount -1) ? forks . get (0) : forks . get ( i +1)) ,
14 forks . get ( i ));
15 phils . add ( phil );
16 phil . start ();
17 }
18 }
19
20 public void start () {
21 for ( ActorRef phil : phils ) {
22 phil . sendOneWay ( Philosopher . THINK );
23 }
24 }
25
26 public void stop () {
27 for ( ActorRef phil : phils ) {
28 phil . stop ();
29 }
30 for ( ActorRef fork : forks ) {
31 fork . stop ();
32 }
33 О.В. Сухорослов
} () 06 Разбор ДЗ №1 30.03.2011 21 / 32
22. Main
1 public static void main ( String [] args ) {
2 Table table = new Table (5);
3 table . start ();
4
5 try {
6 Thread . sleep (60000);
7 } catch ( I n t e r r u p t e d E x c e p t i o n e ) {}
8
9 table . stop ();
10 }
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 22 / 32
23. 2
Software Transactional Memory (STM)
Механизм управления доступом к общим данным в памяти,
аналогичный транзакциям баз данных
Atomicity, Consistency, Isolation, (Durability)
Optimistic concurrency
Реализации
Haskell, Clojure, ScalaSTM...
2
http://en.wikipedia.org/wiki/Software_transactional_memory
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 23 / 32
24. 3
ScalaSTM
1 class Fork { val inUse = Ref ( false ) }
2
3 def meal ( left : Fork , right : Fork ) {
4 // thinking
5 atomic { implicit txn = >
6 if ( left . inUse () || right . inUse ())
7 retry // forks are not both ready , wait
8 left . inUse () = true
9 right . inUse () = true
10 }
11 // eating
12 atomic { implicit txn = >
13 left . inUse () = false
14 right . inUse () = false
15 }
16 }
3
http://nbronson.github.com/scala-stm/philosophers.html
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 24 / 32
26. Характерные времена
Загрузки страницы
Извлечения ссылок
Сохранения страницы на диск
Что имеет смысл распараллеливать?
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 26 / 32
28. В чем причина?
1 public static Set < URL > getLinks ( URL url , String content ) {
2 Set < URL > links = new HashSet < URL >();
3 Matcher matcher = linkPattern . matcher ( content );
4 while ( matcher . find ()) {
5 try {
6 URL link = new URL ( url , matcher . group (1));
7 links . add ( link );
8 } catch ( M a l f o r m e d U R L E x c e p t i o n e ) {}
9 }
10 return links ;
11 }
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 28 / 32
29. Решения
Блокирующая очередь страниц для загрузки + set для
посещенных страниц
Управление потоками
Одноразовый поток на страницу
Число потоков ограничено / неограничено
Повторно используемые потоки
Собственный пул потоков / Executor
Поток порождает дочерние потоки
Специализация потоков
Загрузка, парсинг, сохранение на диск, добавление новых ссылок в
очередь
Что делают рабочие потоки, а что - главный?
Синхронизация между уровнями поиска
Две очереди для загружаемых страниц и новых ссылок
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 29 / 32
30. Детали реализации
Использование ExecutorCompletionService4 для получения
результатов заданий
Ожидание завершения всех заданий5
ExecutorService.awaitTermination()
ExecutorService.invokeAll()
ExecutorCompletionService
Блокирующий Executor для предотвращения переполнения
памяти6
Не забываем про executor.shutdown()
4
http://download.oracle.com/javase/6/docs/api/java/util/concurrent/ExecutorCompletionService.html
5
http://stackoverflow.com/questions/3269445/executorservice-how-to-wait-for-all-tasks-to-finish
6
http://today.java.net/pub/a/today/2008/10/23/creating-a-notifying-blocking-thread-
poolexecutor.html
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 30 / 32
31. Детали реализации
Thread safe set
CopyOnWriteArraySet
ConcurrentSkipListSet
Collections.synchronizedSet(Set<T> s)
Collections.newSetFromMap(new
ConcurrentHashMap<Object,Boolean>())
Использование одного BufferedWriter несколькими потоками
thread safe
Таймауты соединений в getContent()
conn.setConnectTimeout(), conn.setReadTimeout()
О.В. Сухорослов () 06 Разбор ДЗ №1 30.03.2011 31 / 32