# L3 GRUPA 2 ## Zadanie 1 :::success Autor: Kamila Goszcz ::: ```Java= class MergeSort implements Runnable { protected int help_arr[]; protected int arr[]; protected int l, r; final int SMALL = 20; MergeSort(int arr[], int help[], int l, int r) { this.arr = arr; this.help_arr = help; this.l = l; this.r = r; } private void merge(int left, int middle, int right) { int left_index = left; int right_index = middle + 1; for (int i = left; i <= right; i++) { this.help_arr[i] = arr[i]; } int index = left; while (left_index < middle + 1 && right_index < right + 1) { if (help_arr[left_index] <= help_arr[right_index]) { arr[index] = help_arr[left_index]; left_index += 1; } else { arr[index] = help_arr[right_index]; right_index += 1; } index += 1; } while (left_index < middle + 1) { arr[index] = help_arr[left_index]; index += 1; left_index += 1; } while (right_index < right + 1) { arr[index] = help_arr[right_index]; index += 1; right_index += 1; } } public void sort(int left, int right) { if (left < right) { int middle = (left + right) / 2; sort(left, middle); sort(middle + 1, right); merge(left, middle, right); } } @Override public void run() { if (this.l < this.r) { int m = (this.l + this.r) / 2; MergeSort left = new MergeSort(arr, help_arr, this.l, m); MergeSort right = new MergeSort(arr, help_arr, m + 1, this.r); Thread t1 = new Thread(left); Thread t2 = new Thread(right); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } this.merge(this.l, m, this.r); } } } ``` ## Zadanie 2 :::success Autor: Adam Jarząbek ::: ```Java= class MergeSort implements Runnable { public static final int MIN_SIZE_TO_DO_MULTITHREADING = 4; protected int array[]; protected int arrayCopy[]; protected int startIndex, endIndex; MergeSort(int array[], int arrayCopy[], int startIndex, int endIndex) { this.array = array; this.startIndex = startIndex; this.endIndex = endIndex; this.arrayCopy = arrayCopy; } private void merge(int leftIndex, int middleIndex, int rightIndex) { int leftArrayPivot = leftIndex; int rightArrayPivot = middleIndex + 1; int resultArrayPivot = leftIndex; while (leftArrayPivot <= middleIndex && rightArrayPivot <= rightIndex) { if (arrayCopy[leftArrayPivot] <= arrayCopy[rightArrayPivot]) { array[resultArrayPivot] = arrayCopy[leftArrayPivot]; leftArrayPivot += 1; } else { array[resultArrayPivot] = arrayCopy[rightArrayPivot]; rightArrayPivot += 1; } resultArrayPivot += 1; } while (leftArrayPivot <= middleIndex) { array[resultArrayPivot] = arrayCopy[leftArrayPivot]; resultArrayPivot += 1; leftArrayPivot += 1; } while (rightArrayPivot <= rightIndex) { array[resultArrayPivot] = arrayCopy[rightArrayPivot]; resultArrayPivot += 1; rightArrayPivot += 1; } updateArrayCopy(startIndex, endIndex); } public void updateArrayCopy(int startIndex, int endIndex) { for (int i = startIndex; i <= endIndex; i++) { arrayCopy[i] = array[i]; } } public void sort(int startIndex, int endIndex) { if (startIndex < endIndex) { int middleIndex = (startIndex + endIndex) / 2; sort(startIndex, middleIndex); sort(middleIndex + 1, endIndex); merge(startIndex, middleIndex, endIndex); } } @Override public void run() { System.out.println("Starting thread..."); if (this.startIndex < this.endIndex) { if (endIndex - startIndex < MIN_SIZE_TO_DO_MULTITHREADING) { sort(startIndex, endIndex); } else { int middleIndex = (startIndex + endIndex) / 2; MergeSort leftMerge = new MergeSort(array, arrayCopy, startIndex, middleIndex); MergeSort rightMerge = new MergeSort(array, arrayCopy, middleIndex + 1, endIndex); Thread leftHalfThread = new Thread(leftMerge); leftHalfThread.start(); rightMerge.sort(middleIndex + 1, endIndex); try { leftHalfThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } merge(startIndex, middleIndex, endIndex); } } } } public class RookieMergeSort { public static void main(String[] args) { int arr[] = { 8, 5, 6, 7, 4, 3, 1, 2}; MergeSort mergeSort = new MergeSort(arr, new int[8], 0, 7); mergeSort.updateArrayCopy(0, 7); Thread thread = new Thread(mergeSort); thread.start(); try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } for (int element : arr) System.out.printf("%d ", element); } } ``` ## Zadanie 3 :::success Autor: Rafał Starypan ![](https://i.imgur.com/FpxqLvs.png) ```java=1 package zad3; class ThreadCounter { private int currentThreadCount; private int maxThreadCount; public ThreadCounter(int maxThreadCount) { this.currentThreadCount = 0; this.maxThreadCount = maxThreadCount; } public synchronized int getCurrentThreadCount() { return currentThreadCount; } public synchronized int incrementAndGetCurrentThreadCount() { if (currentThreadCount < maxThreadCount) { int temp = currentThreadCount; currentThreadCount = temp + 1; return temp; } return currentThreadCount; } } ``` ```java=1 package zad3; class MergeSort implements Runnable { protected int arr[]; protected int l, r; protected ThreadCounter counter; private int sharedArr[]; MergeSort(int arr[], int l, int r, ThreadCounter counter) { this.arr = arr; this.l = l; this.r = r; this.sharedArr = new int [arr.length]; this.counter = counter; } private void merge(int l, int m, int r) { for (int i = l; i <= r; i++) { sharedArr[i] = arr[i]; } int i = l; int j = m + 1; int sortedPos = l; while (i <= m && j <= r) { if (sharedArr[i] <= sharedArr[j]) { arr[sortedPos] = sharedArr[i]; i++; } else { arr[sortedPos] = sharedArr[j]; j++; } sortedPos++; } while (i <= m) { arr[sortedPos] = sharedArr[i]; sortedPos++; i++; } while (j <= r) { arr[sortedPos] = sharedArr[j]; sortedPos++; j++; } } private void sort(int l, int r) { if (l < r) { int m = (l + r) / 2; sort(l, m); sort(m + 1, r); merge(l, m, r); } } private void sortWithThreading() { int m = (l + r) / 2; MergeSort L = new MergeSort(arr, l, m, counter); MergeSort R = new MergeSort(arr, m + 1, r, counter); Thread t1 = new Thread(L); Thread t2 = new Thread(R); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } this.merge(l, m, r); } @Override public void run() { System.out.println(Thread.currentThread().getName()); if (l < r) { int m = (l + r) / 2; MergeSort left = new MergeSort(arr, l, m, counter); MergeSort right = new MergeSort(arr, m + 1, r, counter); Thread t1 = null; Thread t2 = null; try { if (counter.incrementAndGetCurrentThreadCount() < RookieMergeSort.MAX_THREAD_COUNT - 1) { t1 = new Thread(left); t1.start(); } else { sort(l, m); } if (counter.incrementAndGetCurrentThreadCount() < RookieMergeSort.MAX_THREAD_COUNT - 1) { t2 = new Thread(right); t2.start(); } else { sort(m + 1, r); } if (t1 != null) { t1.join(); } if (t2 != null) { t2.join(); } } catch (InterruptedException e) { e.printStackTrace(); } this.merge(l, m, r); } } } ``` ```java=1 package zad3; public class RookieMergeSort { public static final int MAX_THREAD_COUNT = 4; public static void main(String[] args) { int arr[] = {5, 10, 6, 7, 8, 9, 11, 4, 3, 2}; ThreadCounter counter = new ThreadCounter(MAX_THREAD_COUNT); MergeSort w = new MergeSort(arr, 0, arr.length - 1, counter); Thread t = new Thread(w); t.start(); try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < arr.length; i++) System.out.printf("%d ", arr[i]); System.out.println(counter.getCurrentThreadCount()); } } ``` ::: ## Zadanie 4 :::success Autor Zuzanna Kania ![](https://i.imgur.com/hqrlrv0.png) ```java=1 class LongestSubseq implements Runnable { protected int arr[]; protected int left, right, longestLeftIdx, longestRightIdx, leftEndIdx, rightBeginIdx; LongestSubseq(int arr[], int left, int right) { this.arr = arr; this.left = left; this.right = right; this.longestLeftIdx = right; this.longestRightIdx = left; this.leftEndIdx = left; this.rightBeginIdx = right; } private void merge(int left, int mid, int right, LongestSubseq leftPart, LongestSubseq rightPart) { // jeśli koniec lewej połowy == początek prawej połowy if (this.arr[mid] == this.arr[mid + 1]) { this.longestLeftIdx = leftPart.rightBeginIdx; this.longestRightIdx = rightPart.leftEndIdx; } // jeśli w lewej połowie mamy dłuższy ciąg if (leftPart.longestRightIdx - leftPart.longestLeftIdx + 1 > this.longestRightIdx - this.longestLeftIdx + 1) { this.longestLeftIdx = leftPart.longestLeftIdx; this.longestRightIdx = leftPart.longestRightIdx; } // jeśli w prawej połowie mamy dłuższy ciąg if (rightPart.longestRightIdx - rightPart.longestLeftIdx + 1 > this.longestRightIdx - this.longestLeftIdx + 1) { this.longestLeftIdx = rightPart.longestLeftIdx; this.longestRightIdx = rightPart.longestRightIdx; } // aktualizuj koniec lewej części if (leftPart.leftEndIdx == mid && this.arr[mid] == this.arr[rightPart.leftEndIdx]) { this.leftEndIdx = rightPart.leftEndIdx; } else { this.leftEndIdx = leftPart.leftEndIdx; } // aktualizuj początek prawej części if (rightPart.rightBeginIdx == mid + 1 && this.arr[mid + 1] == this.arr[leftPart.rightBeginIdx]) { this.rightBeginIdx = leftPart.rightBeginIdx; } else { this.rightBeginIdx = rightPart.rightBeginIdx; } } public int[] getResult() { int resArr[] = new int[this.longestRightIdx - this.longestLeftIdx + 1]; int j = 0; for (int i = this.longestLeftIdx; i <= this.longestRightIdx; i++) { resArr[j] = this.arr[i]; j++; } return resArr; } @Override public void run() { if (this.left < this.right) { int m = (this.left + this.right) / 2; LongestSubseq leftSub = new LongestSubseq(arr, this.left, m); LongestSubseq rightSub = new LongestSubseq(arr, m + 1, this.right); Thread t1 = new Thread(leftSub); Thread t2 = new Thread(rightSub); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } this.merge(this.left, m, this.right, leftSub, rightSub); } } } public class zadanie4 { public static void main(String[] args) { int arr[] = { 1, 2, 1, 2, 1, 2, 1, 2, 3, 3, 3 }; LongestSubseq w = new LongestSubseq(arr, 0, arr.length - 1); Thread t = new Thread(w); t.start(); try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); } int res[] = w.getResult(); for (int i = 0; i < res.length; i++) System.out.printf("%d ", res[i]); } } ``` ::: ## Zadanie 5 :::success Autor: Hubert Obrzut ::: ```java= class TaskPool { ConcurrentLinkedQueue<Task> queue; int tasksLeft; TaskPool() { this.tasksLeft = 0; queue = new ConcurrentLinkedQueue<Task>(); } synchronized void updateTasksLeft(int delta) { tasksLeft += delta; } } abstract class Task { abstract boolean isReady(); abstract void execute(); void merge(int[] arr, int[] buf, int l, int m, int r) { for (int i = l; i <= r; ++i) buf[i] = arr[i]; int i = l, j = m+1, k = l; while (i <= m && j <= r) arr[k++] = (buf[i] <= buf[j]) ? buf[i++] : buf[j++]; while (i <= m) arr[k++] = buf[i++]; while (j <= r) arr[k++] = buf[j++]; } } class SortTask extends Task { int l, r; int[] arr, buf; MergeTask parent; TaskPool pool; static final int SEQUENTIAL_THRESHOLD = 50; SortTask(int l, int r, int[] arr, int[] buf, TaskPool pool, MergeTask parent) { this.l = l; this.r = r; this.arr = arr; this.buf = buf; this.pool = pool; this.parent = parent; } void execute() { if (r-l+1 <= SEQUENTIAL_THRESHOLD) { sort_sequential(l, r); if (parent != null) parent.childCompleted(); } else { int m = (l+r) / 2; MergeTask mergeTask = new MergeTask(l, m, r, arr, buf, pool, parent); pool.queue.offer(new SortTask(l, m, arr, buf, pool, mergeTask)); pool.queue.offer(new SortTask(m+1, r, arr, buf, pool, mergeTask)); pool.queue.offer(mergeTask); pool.updateTasksLeft(3); } pool.updateTasksLeft(-1); } void sort_sequential(int l, int r) { if (l < r) { int m = (l+r) / 2; sort_sequential(l, m); sort_sequential(m+1, r); merge(arr, buf, l, m, r); } } boolean isReady() { return true; } } class MergeTask extends Task { int l, m, r; int childCompleted; int[] arr, buf; TaskPool pool; MergeTask parent; MergeTask(int l, int m, int r, int arr[], int buf[], TaskPool pool, MergeTask parent) { this.l = l; this.m = m; this.r = r; this.childCompleted = 0; this.arr = arr; this.buf = buf; this.pool = pool; this.parent = parent; } void execute() { merge(arr, buf, l, m, r); if (parent != null) parent.childCompleted(); pool.updateTasksLeft(-1); } boolean isReady() { return childCompleted == 2; } synchronized void childCompleted() { ++childCompleted; } } class MergeSort implements Runnable { int[] arr, buf; TaskPool pool; MergeSort(int[] arr, int[] buf, TaskPool pool) { this.arr = arr; this.buf = buf; this.pool = pool; } @Override public void run() { while (pool.tasksLeft > 0) { Task task = pool.queue.poll(); if (task != null) { if (task.isReady()) task.execute(); else pool.queue.offer(task); } } } } public class PoolOfThreads { static final int NUM_THREADS = 4; public static void main(String[] args) { int n = 10000; int[] arr = new int[n]; int[] buf = new int[n]; Random random = new Random(); for (int i = 0; i < n; ++i) arr[i] = random.nextInt(n); TaskPool pool = new TaskPool(); Task initialTask = new SortTask(0, n-1, arr, buf, pool, null); pool.queue.offer(initialTask); pool.tasksLeft = 1; Thread[] threads = new Thread[NUM_THREADS]; for (int i = 0; i < NUM_THREADS; ++i) { MergeSort w = new MergeSort(arr, buf, pool); threads[i] = new Thread(w); threads[i].start(); } try { for (int i = 0; i < NUM_THREADS; ++i) threads[i].join(); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < n-1; ++i) assert arr[i] <= arr[i+1]; // for (int i = 0; i < n; ++i) // System.out.printf("%d ", arr[i]); } } ``` ## Zadanie 6 :::success Autor: Jan Jankowicz ::: ![](https://i.imgur.com/eGhG8ly.png) Zakończenie wątku polega na skonsumowaniu przez niego eventu o specjalnym typie - TERMINATE. Jeśli wątek pobierze takie zadanie z kolejki, to przed zakończeniem swojego działania wrzuci na kolejkę taki sam event, aby poinformować następny czekający wątek o zakończeniu sortowania. Pierwsze wrzucenie zadania kończącego na kolejkę dzieje się w momencie skończenia zadania scalania całości tablicy. Empiryczne porównanie średnich czasów działania merge sorta zaimplementowanego z użyciem LinkedBlockingQueue i z użyciem ConcurrentLinkedQueue wskazuje na większą wydajność (przynajmniej w tym zastosowaniu) tej pierwszej kolejki. Z doświadczeń wynika, że użycie LinkedBlockingQueue zwiększa szybkość działania programu około dwukrotnie. ```java= import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; public class RookieMergeSortBlockingQueue { public static void main(String[] args) { sortAndPrintOut(RookieMergeSortUtils.generateArray()); long timeSum = 0; for (int i = 0; i < 100; i++) { timeSum += sortAndPrintOut(RookieMergeSortUtils.generateRandomArray(2500)); } System.out.println("Average time: " + timeSum/100); } private static long sortAndPrintOut(int[] arrayToSort) { long startTime = System.currentTimeMillis(); int size = arrayToSort.length; BlockingQueue<MergeSortTask> taskQueue = new LinkedBlockingQueue<>(); Set<ArrayRange> sortedArrayRanges = ConcurrentHashMap.newKeySet(); taskQueue.offer(new MergeSortTask(MergeSortTask.MergeSortTaskType.SORT, new ArrayRange(0, size - 1))); List<Thread> workers = new ArrayList<>(); for (int i = 0; i < 5; i++) { workers.add(new Thread(new MergeSortBlockingQueueWorker(arrayToSort, taskQueue, sortedArrayRanges))); } workers.forEach(Thread::start); try { for (Thread worker : workers) { worker.join(); } } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < size; i++) System.out.printf("%d ", arrayToSort[i]); System.out.print("\n"); long endTime = System.currentTimeMillis(); System.out.printf("Sort time: %d\n", endTime - startTime); return endTime - startTime; } } class MergeSortBlockingQueueWorker implements Runnable { protected int sortedArray[]; protected final BlockingQueue<MergeSortTask> taskQueue; protected final Set<ArrayRange> sortedArrayRanges; protected MergeSortBlockingQueueWorker(int sortedArray[], BlockingQueue<MergeSortTask> taskQueue, Set<ArrayRange> sortedArrayRanges) { this.sortedArray = sortedArray; this.taskQueue = taskQueue; this.sortedArrayRanges = sortedArrayRanges; } private void merge(int leftSortingBound, int splitIndex, int rightSortingBound) { int[] leftArray = getSubarray(leftSortingBound, splitIndex); int[] rightArray = getSubarray(splitIndex + 1, rightSortingBound); mergeSortedArrays(leftArray, rightArray, leftSortingBound); } private int[] getSubarray(int leftSortingBound, int rightSortingBound) { int size = rightSortingBound - leftSortingBound + 1; int[] subarray = new int[size]; copyArray(sortedArray, leftSortingBound, subarray, 0, size); return subarray; } private static void copyArray(int sourceArray[], int sourceArrayFirstElementIndex, int targetArray[], int targetArrayFirstElementIndex, int size) { for (int i = 0; i < size; i++) { targetArray[targetArrayFirstElementIndex + i] = sourceArray[sourceArrayFirstElementIndex + i]; } } private void mergeSortedArrays(int[] leftArray, int[] rightArray, int leftSortingBound) { int leftArraySize = leftArray.length; int rightArraySize = rightArray.length; int leftArrayCurrentMergingIndex = 0, rightArrayCurrentMergingIndex = 0; int resultArrayCurrentMergingIndex = leftSortingBound; while (leftArrayCurrentMergingIndex < leftArraySize && rightArrayCurrentMergingIndex < rightArraySize) { if (leftArray[leftArrayCurrentMergingIndex] <= rightArray[rightArrayCurrentMergingIndex]) { sortedArray[resultArrayCurrentMergingIndex] = leftArray[leftArrayCurrentMergingIndex]; leftArrayCurrentMergingIndex += 1; } else { sortedArray[resultArrayCurrentMergingIndex] = rightArray[rightArrayCurrentMergingIndex]; rightArrayCurrentMergingIndex += 1; } resultArrayCurrentMergingIndex += 1; } while (leftArrayCurrentMergingIndex < leftArraySize) { sortedArray[resultArrayCurrentMergingIndex] = leftArray[leftArrayCurrentMergingIndex]; resultArrayCurrentMergingIndex += 1; leftArrayCurrentMergingIndex += 1; } while (rightArrayCurrentMergingIndex < rightArraySize) { sortedArray[resultArrayCurrentMergingIndex] = rightArray[rightArrayCurrentMergingIndex]; resultArrayCurrentMergingIndex += 1; rightArrayCurrentMergingIndex += 1; } } @Override public void run() { MergeSortTask task; while (true) { try { task = taskQueue.take(); if (MergeSortTask.MergeSortTaskType.TERMINATE.equals(task.type)) { taskQueue.offer(MergeSortTask.termination()); break; } else { handleTask(task); } } catch (InterruptedException e) { e.printStackTrace(); } } } private void handleTask(MergeSortTask task) { switch (task.type) { case SORT: if (task.arrayRange.leftBound < task.arrayRange.rightBound) { taskQueue.offer(new MergeSortTask(MergeSortTask.MergeSortTaskType.SORT, task.arrayRange.getLeftHalf())); taskQueue.offer(new MergeSortTask(MergeSortTask.MergeSortTaskType.SORT, task.arrayRange.getRightHalf())); taskQueue.offer(new MergeSortTask(MergeSortTask.MergeSortTaskType.MERGE, task.arrayRange)); } else { sortedArrayRanges.add(task.arrayRange); } break; case MERGE: if (sortedArrayRanges.contains(task.arrayRange.getLeftHalf()) && sortedArrayRanges.contains(task.arrayRange.getRightHalf())) { merge(task.arrayRange.leftBound, ArrayRange.getMedianIndex(task.arrayRange), task.arrayRange.rightBound); sortedArrayRanges.add(task.arrayRange); if (task.arrayRange.leftBound == 0 && task.arrayRange.rightBound == sortedArray.length - 1) { taskQueue.offer(MergeSortTask.termination()); } } else { taskQueue.offer(task); } break; case TERMINATE: taskQueue.offer(MergeSortTask.termination()); break; } } } class MergeSortTask { final MergeSortTaskType type; final ArrayRange arrayRange; MergeSortTask(MergeSortTaskType type, ArrayRange arrayRange) { this.type = type; this.arrayRange = arrayRange; } public static MergeSortTask termination() { return new MergeSortTask(MergeSortTaskType.TERMINATE, null); } enum MergeSortTaskType { SORT, MERGE, TERMINATE } @Override public String toString() { if (MergeSortTaskType.TERMINATE.equals(type)) { return "Task - TERMINATE"; } else { return String.format("Task - %s array in range %s", type, this.arrayRange); } } } class ArrayRange { final int leftBound; final int rightBound; ArrayRange(int leftBound, int rightBound) { this.leftBound = leftBound; this.rightBound = rightBound; } public ArrayRange getLeftHalf() { int medianIndex = getMedianIndex(this); return new ArrayRange(leftBound, medianIndex); } public ArrayRange getRightHalf() { int medianIndex = getMedianIndex(this); return new ArrayRange(medianIndex + 1, rightBound); } public static int getMedianIndex(ArrayRange arrayRange) { return (arrayRange.leftBound + arrayRange.rightBound) / 2; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ArrayRange that = (ArrayRange) o; return leftBound == that.leftBound && rightBound == that.rightBound; } @Override public int hashCode() { return Objects.hash(leftBound, rightBound); } @Override public String toString() { return String.format("[%d, %d]", this.leftBound, this.rightBound); } } ``` ## Zadanie 7 :::success Autor: Maria Szlasa ::: ![](https://i.imgur.com/dJZqDQF.png) ### Niezakleszczenie Zakleszczenie może zajść, jeśli wszystkie wątki zostaną zatrzymane w którejś pętli. W pętki do-while w for spradzamy, czy jakiś wątek o numerze mniejszym ma podniesioną flagę. Jeśli ma to my opuszczamy swoją flagę i czekamy. Wtedy zawsze do dalszej części algorytmu przejdzie wątek o najniższym numerze, który chce wywołać lock. W pętli for sprawdzamy, czy jakieś wątki o większym numerze, które przeszły przez do-while chcą wejść do CS. Jeśli tak to czekamy aż one wykonają sekcje. Dlaczego tak jest? Jeśli nie ma żadnego wątku w do-while, to wyjdzie z tej pętli. Jeśli w do-while są jakieś wątki o większym numerze to opuszczą one swoją flagę czekając na mniejsze. ## Zadanie 8 :::success Autor: Magdalena Rzepka ::: Załóżmy, że mamy wątki A, B i C oraz dwie lokacje L~A~ i L~B~. Każdy wątek przed wejściem do sekcji krytycznej musi zapisać dane przynajmniej w jednej współdzielonej lokacji. - gdyby wątek mógł wejść bez zapisania czegokolwiek, to inny wątek nie mógłby sprawdzić, że ktoś już jest w sekcji krytycznej i sam mógłby tam wejść - stan lokacji wskazywałby, że nikogo tam nie ma -> stan niespójności Będziemy rozważać sytuację, w której wątek B 3 razy będzie chciał wejść do sekcji krytyczej. Lokalizacje L~A~ i L~B~ są początkowo puste (wskazują, że nikogo nie ma i nikt nie chce wejść do sekcji krytycznej). Skoro przed wejściem do sekcji krytycznej wątek B musi zapisać dane przynajmniej w jednej lokalizacji, to musi przynajmniej dwa razy zapisać w pewnej lokalizacji jako pierwszej (nazwijmy ją L~B~). **Uruchamiamy wątek B i zatrzymujemy go w momencie, w którym chce zapisać dane do L~B~ jako pierwszej lokacji po raz pierwszy.** - lokalizacje L~A~ i L~B~ są puste, bo wątek B wychodząc z sekcji krytycznej robi jakiegoś rodzaju unlock() i czyści lokalizacje, których używa, a te nie używane nie są przez nic nadpisywane. Wątek A musi zapisać dane w lokalizacji L~A~ (może również zapisać w L~B~). - gdyby wątek A zapisał dane tylko w lokalizacji L~B~ i wszedł do sekcji krytycznej to wątek B po uruchomieniu nadpisałby dane w L~B~ i również wszedł do sekcji krytycznej, bo obecność A zostałaby zapomniana. **Uruchamiamy wątek A i zatrzymujemy go, przed zapisaniem danych w lokalizacji L~A~.** - lokaliacja L~A~ jest pusta, ale lokalizacja L~B~ może zawierać jakieś dane. **Uruchamiamy wątek B, który zapisuje dane do L~B~ (może też do L~A~). Wątek B wychodząc z sekcji krytycznej robi unlock() i czyści L~B~ (jeżeli nadpisał też L~A~ to czyści L~A~).** - lokacje L~A~ i L~B~ są puste. **Zatrzymujemy wątek B, kiedy chce zapisać do L~B~ jako pierwszej lokacji po raz drugi.** - lokacje L~A~ i L~B~ są puste, bo gdyby wątek B wchodził do sekcji krytycznej to by sprzątał za sobą a nic innego co nadpisuje nie byłoby uruchomione. **Uruchamiamy wątek C. Wątek C zapisuje dane w przynajmniej jednej lokacji, wchodzi do sekcji krytycznej i zostaje zatrzymany.** **Uruchamiamy wątek A i B. Wątek A nadpisuje lokacje L~A~ a wątek B lokacje L~B~.** W tym momencie nie został nam ślad do wątku C i żaden z wątków nie wie, że C znajduje się w sekcji krytycznej. Któryś z wątków w końcu będzie mógł wejść do sekcji krytycznej (bo rozważamy algorytm nie zakleszczający się) i w sekcji krytycznej będą 2 wątki.