2. Синхронизация
Безопасный доступ к общим данным
Взаимное исключение и memory barrier (см. лекцию 1)
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 2 / 61
3. Синхронизация
Безопасный доступ к общим данным
Взаимное исключение и memory barrier (см. лекцию 1)
Координация действий между потоками
Условная синхронизация
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 2 / 61
4. Поиск шаблона в тексте
lecture2.textsearch.SeqTextSearch
1 public class SeqTextSearch {
2 ...
3
4 public static void main ( String [] args ) {
5 long start = System . c u r r e n t T i m e M i l l i s ();
6 try {
7 String filePath = args [0];
8 String pattern = args [1];
9
10 List < String > text = readFile ( filePath );
11 List < String > results = search ( text , pattern );
12
13 System . out . println (
14 " Found " + results . size () + " matches : " );
15 ...
16 } catch ( Exception e ) {
17 e . p ri nt S ta ck T ra ce ();
18 }
19 long end = System . c u r r e n t T i m e M i l l i s ();
20 System . out . println ( " Total time : " + ( end - start ) + " ms " );
21 }
22 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 3 / 61
5. Запуск
XML-файл, 370 Mb
Pattern: ‘[w-.]+@([w-]+.){1,2}[a-zA-Z]{2,4}‘
Read time: 8333 ms
Search time: 44016 ms
Found 98 matches
Total time: 52352 ms
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 4 / 61
6. OutOfMemoryError
Exception in thread " main " java . lang . OutOfMemoryError : Java heap space
at java . util . Arrays . copyOf ( Arrays . java :2760)
at java . util . Arrays . copyOf ( Arrays . java :2734)
at java . util . ArrayList . ensureCapacity ( ArrayList . java :167)
at java . util . ArrayList . add ( ArrayList . java :351)
at lecture2 . textsearch . SeqTextSearch . readFile ( SeqTextSearch . java :22)
at lecture2 . textsearch . SeqTextSearch . main ( SeqTextSearch . java :54)
java -Xms<initial heap size> -Xmx<maximum heap size>
java -Xms256m -Xmx256m
java -Xms2g -Xmx2g
Eclipse: Run / Arguments / VM arguments
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 5 / 61
8. SearchTask
lecture2.textsearch.ParallelTextSearch1.SearchTask
1 static class SearchTask implements Runnable {
2
3 private List < String > text ;
4 private int from , to ;
5 private Pattern regex ;
6 public List < String > results ;
7
8 public SearchTask ( List < String > text , int from , int to ,
9 Pattern regex ) {
10 ...
11 }
12
13 public void run () {
14 Matcher matcher ;
15 for ( int i = from ; i <= to ; i ++) {
16 matcher = regex . matcher ( text . get ( i ));
17 while ( matcher . find ()) {
18 results . add (...);
19 }
20 }
21 }
22 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 7 / 61
10. Запуск ParallelTextSearch1
Read time: 8437 ms Read time: 8333 ms
Search time: 26034 ms Search time: 44016 ms
Found 98 matches Found 98 matches
Total time: 34472 ms Total time: 52352 ms
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 9 / 61
13. BaseBoundedBuffer
lecture2.textsearch.BaseBoundedBuffer
1 public abstract class BaseBoundedBuffer <T > {
2
3 private final T [] buf ;
4 private int tail ;
5 private int head ;
6 private int count ;
7
8 protected Base BoundedBuffer ( int capacity ) {
9 this . buf = ( T []) new Object [ capacity ];
10 }
11
12 protected synchronized final void doPut ( T v ) {
13 buf [ tail ] = v ;
14 if (++ tail == buf . length )
15 tail = 0;
16 ++ count ;
17 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 12 / 61
14. BaseBoundedBuffer (2)
lecture2.textsearch.BaseBoundedBuffer
1 protected synchronized final T doTake () {
2 T v = buf [ head ];
3 buf [ head ] = null ;
4 if (++ head == buf . length )
5 head = 0;
6 -- count ;
7 return v ;
8 }
9
10 public synchronized final boolean isFull () {
11 return count == buf . length ;
12 }
13
14 public synchronized final boolean isEmpty () {
15 return count == 0;
16 }
17
18 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 13 / 61
15. PollingQueue
lecture2.textsearch.PollingQueue
1 public class PollingQueue <T > extends BaseBoundedBuffer <T > {
2 ...
3 public void put ( T v ) throws I n t e r r u p t e d E x c e p t i o n {
4 while ( true ) {
5 synchronized ( this ) {
6 if (! isFull ()) {
7 doPut ( v );
8 return ;
9 }
10 }
11 Thread . sleep ( SLEEP_TIME );
12 }
13 }
14
15 public T take () throws I n t e r r u p t e d E x c e p t i o n {
16 while ( true ) {
17 synchronized ( this ) {
18 if (! isEmpty ())
19 return doTake ();
20 }
21 Thread . sleep ( SLEEP_TIME );
22 }
23 }
24 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 14 / 61
16. WaitNotifyQueue
lecture2.textsearch.WaitNotifyQueue
1 public class WaitNotifyQueue <T > extends BaseBoundedBuffer <T > {
2
3 public W a it No t if yQ u eu e ( int size ) {
4 super ( size );
5 }
6
7 public synchronized void put ( T v ) throws I n t e r r u p t e d E x c e p t i o n {
8 while ( isFull ())
9 wait ();
10 doPut ( v );
11 notifyAll ();
12 }
13
14 public synchronized T take () throws I n t e r r u p t e d E x c e p t i o n {
15 while ( isEmpty ())
16 wait ();
17 T v = doTake ();
18 notifyAll ();
19 return v ;
20 }
21
22 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 15 / 61
18. ConditionQueue
lecture2.textsearch.ConditionQueue
1 public class ConditionQueue <T > {
2
3 protected final Lock lock = new ReentrantLock ();
4 private final Condition notFull = lock . newCondition ();
5 private final Condition notEmpty = lock . newCondition ();
6 private final T [] buf ;
7 private int tail , head , count ;
8
9 protected Conditio nQueue ( int capacity ) {
10 this . buf = ( T []) new Object [ capacity ];
11 }
12
13 public void put ( T x ) throws I n t e r r u p t e d E x c e p t i o n {
14 lock . lock ();
15 try {
16 while ( count == buf . length )
17 notFull . await ();
18 buf [ tail ] = x ;
19 if (++ tail == buf . length )
20 tail = 0;
21 ++ count ;
22 notEmpty . signal ();
23 } finally {
24 lock . unlock ();
25 }
26 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 17 / 61
19. ConditionQueue (2)
lecture2.textsearch.ConditionQueue
1 public T take () throws I n t e r r u p t e d E x c e p t i o n {
2 lock . lock ();
3 try {
4 while ( count == 0)
5 notEmpty . await ();
6 T x = buf [ head ];
7 buf [ head ] = null ;
8 if (++ head == buf . length )
9 head = 0;
10 -- count ;
11 notFull . signal ();
12 return x ;
13 } finally {
14 lock . unlock ();
15 }
16 }
17 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 18 / 61
20. Reader.run()
lecture2.textsearch.ParallelTextSearch2.Reader.run()
1 try {
2 input = new Buff eredRe ader (
3 new I n p u t S t r e a m R e a d e r ( new Fi le I np u tS tr e am ( filePath )));
4 String line = null ;
5 int lineNum = 0;
6 while (( line = input . readLine ()) != null ) {
7 queue . put ( new TextLine ( lineNum , line ));
8 lineNum ++;
9 }
10 } catch ( IOException e ) {
11 e . p ri nt S ta ck T ra ce ();
12 } finally {
13 queue . put ( null ); // poison pill
14 ...
15 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 19 / 61
21. Searcher.run()
lecture2.textsearch.ParallelTextSearch2.Searcher.run()
1 Matcher matcher ;
2 TextLine line ;
3
4 while ( true ) {
5 line = queue . take ();
6 if ( line != null ) {
7 matcher = regex . matcher ( line . getText ());
8 while ( matcher . find ()) {
9 results . add (...);
10 }
11 } else break ;
12 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 20 / 61
23. Запуск ParallelTextSearch2
Read time: 53228 ms Read time: 8333 ms
Search time: 53246 ms Search time: 44016 ms
Found 98 matches Found 98 matches
Total time: 53251 ms Total time: 52352 ms
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 22 / 61
25. Запуск ParallelTextSearch3
QUEUE_SIZE = 1000
SPLIT_SIZE = 1000
Read time: 38131 ms Read time: 8333 ms
Search time: 42837 ms Search time: 44016 ms
Found 98 matches Found 98 matches
Total time: 42841 ms Total time: 52352 ms
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 24 / 61
28. Запуск ParallelTextSearch4
QUEUE_SIZE = 100
SPLIT_SIZE = 1000
Read time: 41502 ms Read time: 8333 ms
Search time: 41989 ms Search time: 44016 ms
Found 98 matches Found 98 matches
Total time: 41992 ms Total time: 52352 ms
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 27 / 61
29. ParallelTextSearch5
Пул потоков, ведущих поиск в фрагментах текста
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 28 / 61
31. Простые примеры
1 class DirectExecutor implements Executor {
2 public void execute ( Runnable r ) {
3 r . run ();
4 }
5 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 30 / 61
32. Простые примеры
1 class DirectExecutor implements Executor {
2 public void execute ( Runnable r ) {
3 r . run ();
4 }
5 }
1 class Th r e a d P e r T a s k Execu tor implements Executor {
2 public void execute ( Runnable r ) {
3 new Thread ( r ). start ();
4 }
5 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 30 / 61
38. SearchTask
lecture2.textsearch.ParallelTextSearch5.SearchTask
1 static class SearchTask implements Callable < List < String > > {
2
3 private List < TextLine > lines ;
4 private Pattern regex ;
5
6 public SearchTask ( List < TextLine > split , Pattern regex ) {
7 this . lines = split ;
8 this . regex = regex ;
9 }
10
11 public List < String > call () {
12 List < String > results = new ArrayList < String >();
13 for ( TextLine line : lines ) {
14 Matcher matcher = regex . matcher ( line . getText ());
15 while ( matcher . find ()) {
16 results . add (...);
17 }
18 }
19 return results ;
20 }
21 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 34 / 61
39. ParallelTextSearch5
lecture2.textsearch.ParallelTextSearch5.main()
1 exec = Executors . n ew F i x e d T h r e a d P o o l (
2 Runtime . getRuntime (). a v a i l a b l e P r o c e s s o r s ());
3
4 List < Future < List < String > > > results = new ...
5 ...
6 while (( line = input . readLine ()) != null ) {
7 split . add ( new TextLine ( lineNum , line ));
8 if ( split . size () == SPLIT_SIZE ) {
9 Future < List < String > > f =
10 exec . submit ( new SearchTask ( split , regex ));
11 results . add ( f );
12 split = new ArrayList < TextLine >( SPLIT_SIZE );
13 }
14 lineNum ++;
15 }
16
17 if ( split . size () > 0) {
18 Future < List < String > > f = exec . submit (...);
19 results . add ( f );
20 }
21
22 int matchCount = 0;
23 for ( Future < List < String > > result : results ) {
24 matchCount += result . get (). size ();
25 }
26
27 System . out . println ( " Found " + matchCount + " matches " );
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 35 / 61
40. Запуск ParallelTextSearch5
24 ядра
SPLIT_SIZE = 100
24 processors Read time: 8333 ms
Found 98 matches Search time: 44016 ms
Total time: 8485 ms Found 98 matches
Total time: 52352 ms
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 36 / 61
41. Как сделать поиск еще быстрее?
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 37 / 61
46. Алгоритм Флойда
1 for ( k =0; k < N ; k ++) {
2 for ( i =0; i < N ; i ++) {
3 for ( j =0; j < N ; j ++) {
4 A [ i ][ j ] = Math . min ( A [ i ][ j ] , A [ i ][ k ]+ A [ k ][ j ]);
5 }
6 }
7 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 40 / 61
47. FloydCycBarrier
lecture2.floyd.FloydCycBarrier
1 public static void main ( String [] args ) {
2 int [][] A = Graph Genera tor . generate (1000);
3 E xe cu t or Se r vi c e exec =
4 Executors . n e w F i x e d T h r e a d P o o l ( numThreads );
5 try {
6 CyclicBarrier b = new CyclicBarrier ( numThreads + 1);
7
8 for ( int i = 0; i < numThreads ; i ++) {
9 exec . execute ( new FloydTask (A , i , b ));
10 }
11
12 for ( int k = 0; k < A . length ; k ++) {
13 b . await ();
14 // System . out . println (" Iteration " + k );
15 }
16 } catch ( Exception e ) {
17 e . p ri nt S ta ck T ra ce ();
18 } finally {
19 exec . shutdown ();
20 }
21 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 41 / 61
48. FloydTask
lecture2.floyd.FloydCycBarrier.FloydTask
1 static class FloydTask implements Runnable {
2 ...
3
4 public void run () {
5 try {
6 int i , j , k ;
7 for ( k = 0; k < A . length ; k ++) {
8 for ( i = t ; i < A . length ; i += numThreads ) {
9 for ( j = 0; j < A . length ; j ++) {
10 A [ i ][ j ] =
11 Math . min ( A [ i ][ j ] , A [ i ][ k ] + A [ k ][ j ]);
12 }
13 }
14 b . await ();
15 }
16 } catch ( I n t e r r u p t e d E x c e p t i o n e ) {
17 } catch ( B r o k e n B a r r i e r E x c e p t i o n e ) {
18 e . p ri nt S ta ck T ra ce ();
19 }
20 }
21 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 42 / 61
49. Число итераций не известно? (вариант 1)
1 // wait for all threads to finish iteration
2 int myNum = b . await ();
3
4 // last thread checks stop condition
5 if ( myNum == 0) {
6 // check condition , set done flag ...
7 }
8
9 // sync after condition check
10 b . await ();
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 43 / 61
50. Число итераций не известно? (вариант 2)
1 barrier = new CyclicBarrier (N ,
2 new Runnable () {
3 public void run () {
4 // check condition , set done flag
5 }
6 );
Действие выполняется при достижении барьера всеми потоками,
перед тем как разблокировать их
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 44 / 61
51. Домашнее задание (ДЗ №1, Задача 2)
Напишите многопоточный поисковый робот, реализующий обход
Web-графа в ширину и сохраняющий на диск все посещенные
страницы
При запуске роботу передаются URL начальной страницы и
глубина обхода
Робот не должен посещать одну и ту же страницу более одного
раза
Попытайтесь добиться максимальной скорости работы робота,
обоснуйте используемый для этого подход
Заготовка для робота: lecture2.crawler.CrawlerUtils (см. код к
лекции)
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 45 / 61
52. Дополнительный материал
Maurice Herlihy, Nir Shavit. The Art of Multiprocessor Programming.
Morgan Kaufmann, 2008
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 46 / 61
53. Lock
1 public interface Lock {
2 public void lock (); // before entering critical section
3 public void unlock (); // before leaving critical section
4 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 47 / 61
54. LockOne
1 class LockOne implements Lock {
2 private volatile boolean [] flag = new boolean [2]; // ERROR
3 // thread - local index , 0 or 1
4
5 public void lock () {
6 int i = ThreadID . get ();
7 int j = 1 - i ;
8 flag [ i ] = true ;
9 while ( flag [ j ]) {} // wait
10 }
11
12 public void unlock () {
13 int i = ThreadID . get ();
14 flag [ i ] = false ;
15 }
16 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 48 / 61
55. LockTwo
1 class LockTwo implements Lock {
2 private volatile int victim ;
3
4 public void lock () {
5 int i = ThreadID . get ();
6 victim = i ; // let the other go first
7 while ( victim == i ) {} // wait
8 }
9
10 public void unlock () {}
11 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 49 / 61
56. PetersonLock
1 class Peterson implements Lock {
2 // thread - local index , 0 or 1
3 private volatile boolean [] flag = new boolean [2]; // ERROR
4 private volatile int victim ;
5
6 public void lock () {
7 int i = ThreadID . get ();
8 int j = 1 - i ;
9 flag [ i ] = true ; // I ’m interested
10 victim = i ; // you go first
11 while ( flag [ j ] && victim == i ) {}; // wait
12 }
13
14 public void unlock () {
15 int i = ThreadID . get ();
16 flag [ i ] = false ; // I ’m not interested
17 }
18 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 50 / 61
57. Filter Lock
1 class Filter implements Lock {
2 A t o m ic I nt e ge r Ar r ay level ; // volatile int [] level ;
3 A t o m ic I nt e ge r Ar r ay victim ; // volatile int [] victim ;
4
5 public Filter ( int n ) {
6 level = new int [ n ];
7 victim = new int [ n ]; // use 1.. n -1
8 for ( int i = 0; i < n ; i ++) {
9 level [ i ] = 0;
10 }
11 }
12 public void lock () {
13 int me = ThreadID . get ();
14 for ( int i = 1; i < n ; i ++) { // attempt level 1
15 level [ me ] = i ;
16 victim [ i ] = me ;
17 // spin while conflicts exist
18 while (( EXISTS k != me ) ( level [ k ] >= i && victim [ i ] == me )) {};
19 }
20 }
21 public void unlock () {
22 int me = ThreadID . get ();
23 level [ me ] = 0;
24 }
25 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 51 / 61
58. Lamport’s Bakery Algorithm
1 class Bakery implements Lock {
2 volatile boolean [] flag ; // ERROR
3 volatile Label [] label ; // ERROR
4 public Bakery ( int n ) {
5 flag = new boolean [ n ];
6 label = new Label [ n ];
7 for ( int i = 0; i < n ; i ++) {
8 flag [ i ] = false ; label [ i ] = 0;
9 }
10 }
11 public void lock () {
12 int i = ThreadID . get ();
13 flag [ i ] = true ;
14 label [ i ] = max ( label [0] , ... , label [n -1]) + 1;
15 while (( EXISTS k != i )( flag [ k ] && ( label [ k ] , k ) << ( label [ i ] , i ))) {};
16 }
17 public void unlock () {
18 flag [ ThreadID . get ()] = false ;
19 }
20 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 52 / 61
59. Важная теорема
Любой алгоритм, решающий задачу взаимного исключения для N
потоков (с гарантией от deadlock) путем чтения и записи памяти,
должен использовать как минимум N адресов памяти
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 53 / 61
61. Test-And-Set-Lock
1 public class TASLock implements Lock {
2 AtomicBoolean state = new AtomicBoolean ( false );
3
4 public void lock () {
5 while ( state . getAndSet ( true )) {}
6 }
7
8 public void unlock () {
9 state . set ( false );
10 }
11 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 55 / 61
62. Test-Test-And-Set-Lock
1 public class TTASLock implements Lock {
2 AtomicBoolean state = new AtomicBoolean ( false );
3
4 public void lock () {
5 while ( true ) {
6 while ( state . get ()) {};
7 if (! state . getAndSet ( true ))
8 return ;
9 }
10 }
11
12 public void unlock () {
13 state . set ( false );
14 }
15 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 56 / 61
63. BackoffLock
1 public class BackoffLock implements Lock {
2 private AtomicBoolean state = new AtomicBoolean ( false );
3 private static final int MIN_DELAY = ...;
4 private static final int MAX_DELAY = ...;
5 public void lock () {
6 Backoff backoff = new Backoff ( MIN_DELAY , MAX_DELAY );
7 while ( true ) {
8 while ( state . get ()) {};
9 if (! state . getAndSet ( true )) {
10 return ;
11 } else {
12 backoff . backoff ();
13 }
14 }
15 }
16 public void unlock () {
17 state . set ( false );
18 }
19 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 57 / 61
64. Backoff
1 public class Backoff {
2 final int minDelay , maxDelay ;
3 int limit ;
4 final Random random ;
5
6 public Backoff ( int min , int max ) {
7 minDelay = min ;
8 maxDelay = min ;
9 limit = minDelay ;
10 random = new Random ();
11 }
12
13 public void backoff () throws I n t e r r u p t e d E x c e p t i o n {
14 int delay = random . nextInt ( limit );
15 limit = Math . min ( maxDelay , 2 * limit );
16 Thread . sleep ( delay );
17 }
18 }
О.В. Сухорослов 02 Многопоточное программирование
() 24.02.2011 58 / 61