commit ad92b64ce341ea2c7366445bf9befe57c12343f7 Author: Obai Albek <89144251+ObaiAlbek@users.noreply.github.com> Date: Thu Aug 28 14:06:42 2025 +0200 Hand-over-Hand Locking (Java Concurrency Example) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..525681c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target/ +/.classpath +/.project diff --git a/README.md b/README.md new file mode 100644 index 0000000..6f9f415 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# Hand-over-Hand Locking (Java) + +## 📌 Projektbeschreibung +Dieses Projekt implementiert eine **Thread-sichere generische Liste** in Java mithilfe von **Hand-over-Hand Locking** (auch Lock Coupling genannt). +Die Technik wird verwendet, um **Nebenläufigkeit (Concurrency)** zu ermöglichen und gleichzeitig eine feingranulare Synchronisation zu erreichen. + +## ⚙️ Technologien +- **Java 17+** +- **Concurrency & Synchronisation (Locks, Threads)** +- **Maven** + +## ▶️ Ausführen +1. Projekt mit Maven bauen: + ```bash + mvn clean install diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..d4f8887 --- /dev/null +++ b/pom.xml @@ -0,0 +1,62 @@ + + 4.0.0 + pp + pp.A4-HandOverHandLocking + 1.0-SNAPSHOT + jar + + + pp.Main + 10 + UTF-8 + + + + org.junit.jupiter + junit-jupiter + 5.10.0 + test + + + org.projectlombok + lombok + 1.18.30 + provided + + + net.jcip + jcip-annotations + 1.0 + provided + + + + clean compile exec:java + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0 + + + org.apache.maven.plugins + maven-compiler-plugin + 3.9.0 + + + org.codehaus.mojo + exec-maven-plugin + 3.1.0 + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.5.0 + + private + en_US + + + + + diff --git a/src/main/java/pp/Main.java b/src/main/java/pp/Main.java new file mode 100644 index 0000000..b3e8df7 --- /dev/null +++ b/src/main/java/pp/Main.java @@ -0,0 +1,43 @@ +package pp; + + +public class Main { + static SimplifiedList list; + + static Runnable sliceSum(int start, int end, int expected) { + return () -> { + var sum = 0; + for (var i = start; i < end; i++) { + sum += list.get(i); + } + if (sum != expected) { + System.out.println("Fehler in " + + Thread.currentThread().getName() + ": " + sum); + }else + System.out.println("Kein Fehler in " + Thread.currentThread().getName() + ": " + sum); + }; + } + + public static void main(String... args) throws InterruptedException { + list = new ThreadsafeSimplifiedList<>(); + for (int i = 0; i < 5000; i++) { + list.add(i); + } + var thread0 = new Thread(sliceSum(0, 1250, 780625)); + var thread1 = new Thread(sliceSum(1250, 2500, 2343125)); + var thread2 = new Thread(sliceSum(2500, 3750, 3905625)); + var thread3 = new Thread(sliceSum(3750, 5000, 5468125)); + var start = System.currentTimeMillis(); + thread0.start(); + thread1.start(); + thread2.start(); + thread3.start(); + thread0.join(); + thread1.join(); + thread2.join(); + thread3.join(); + System.out.printf("%s: %d ms\n", list.getClass().toString(), + System.currentTimeMillis() - start); + } + +} diff --git a/src/main/java/pp/SimplifiedList.java b/src/main/java/pp/SimplifiedList.java new file mode 100644 index 0000000..d2c7718 --- /dev/null +++ b/src/main/java/pp/SimplifiedList.java @@ -0,0 +1,58 @@ +package pp; + +public interface SimplifiedList { + + /** + * Returns the element at the specified position in this list. + * + * @param index index of the element to return + * @return the element at the specified position in this list + * @throws Exception + */ + public T get(int index); + + /** + * Appends the specified element to the end of this list. There are no + * limitations on what elements may be added to this list. + * + * @param element element to be appended to this list + * @return true + * @see java.util.Collection#add(Object) + * + */ + public boolean add(T element); + + /** + * Replaces the element at the specified position in this list with the + * specified element. + * + * @param index index of the element to replace + * @param element element to be stored at the specified position + * @return the element previously at the specified position + * @throws Exception + */ + public T set(int index, T element); + + /** + * Returns true if this list contains no elements. + * + * @return true if this list contains no elements + */ + public boolean isEmpty(); + + /** + * delayed passing through of parameter + * + * @param element element to pass through + * @return passed though element + * + */ + public default T delay(T element) { + try { + Thread.sleep(10); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + return element; + } +} diff --git a/src/main/java/pp/ThreadsafeSimplifiedList.java b/src/main/java/pp/ThreadsafeSimplifiedList.java new file mode 100644 index 0000000..558e13a --- /dev/null +++ b/src/main/java/pp/ThreadsafeSimplifiedList.java @@ -0,0 +1,135 @@ +package pp; + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +public class ThreadsafeSimplifiedList + implements SimplifiedList { + + private Node head; + private final Lock listLock = new ReentrantLock(); + + private class Node { + private U element; + private Node next; + private final Lock nodeLock; + + private Node(U element, Node prev, Node next) { + super(); + this.element = element; + this.next = next; + this.nodeLock = new ReentrantLock(); + } + } + + /** + * Returns the element at the specified position in this list. + * + * @param index index of the element to return + * @return the element at the specified position in this list + */ + @Override + public T get(int index){ + this.listLock.lock(); + if (this.head == null) { + this.listLock.unlock(); + throw new IndexOutOfBoundsException("The list is empty"); + } + + var ptr = this.head; + ptr.nodeLock.lock(); + this.listLock.unlock(); + for (var i = 0; i < index; i++) { + if (ptr.next != null) { + ptr.next.nodeLock.lock(); + var savePtr = ptr; + ptr = ptr.next; + savePtr.nodeLock.unlock(); + } else { + throw new IndexOutOfBoundsException(index + " out of bounds"); + } + } + + try {return delay(ptr.element);} + finally { ptr.nodeLock.unlock();} + } + + /** + * Appends the specified element to the end of this list. There are no + * limitations on what elements may be added to this list. + * + * @param element element to be appended to this list + * @return true + * @see java.util.Collection#add(Object) + * + */ + @Override + public boolean add(T element) { + this.listLock.lock(); + if (this.head != null) { + var ptr = this.head; + ptr.nodeLock.lock(); + this.listLock.unlock(); + while (ptr.next != null) { + ptr.next.nodeLock.lock(); + var savePtr = ptr; + ptr = ptr.next; + savePtr.nodeLock.unlock(); + } + ptr.next = new Node<>(element, ptr, null); + ptr.nodeLock.unlock(); + } else { + this.head = new Node<>(element, null, null); + this.listLock.unlock(); + } + return true; + } + + /** + * Replaces the element at the specified position in this list with the + * specified element. + * + * @param index index of the element to replace + * @param element element to be stored at the specified position + * @return the element previously at the specified position + */ + @Override + public T set(int index, T element) { + this.listLock.lock(); + if (this.head == null) { + this.listLock.unlock(); + throw new IndexOutOfBoundsException("The list is empty"); + } + var ptr = this.head; + ptr.nodeLock.lock(); + this.listLock.unlock(); + for (var i = 0; i < index; i++) { + if (ptr.next != null) { + ptr.next.nodeLock.lock(); + var savePtr = ptr; + ptr = ptr.next; + savePtr.nodeLock.unlock(); + } else + throw new IndexOutOfBoundsException(index + " out of bounds"); + + } + try { + ptr.element = element; + return element; + } finally { + ptr.nodeLock.unlock(); + } + } + + /** + * Returns true if this list contains no elements. + * + * @return true if this list contains no elements + */ + @Override + public boolean isEmpty() { + this.listLock.lock(); + try { return this.head == null; } + finally { this.listLock.unlock();} + } +}