Java併發程式設計中的鎖機制詳解

省赚客开发者团队發表於2024-07-14

Java併發程式設計中的鎖機制詳解

大家好,我是微賺淘客系統3.0的小編,是個冬天不穿秋褲,天冷也要風度的程式猿!

在Java併發程式設計中,鎖機制是保證執行緒安全的重要手段。鎖的作用是確保同一時刻只有一個執行緒能夠訪問被鎖保護的資源,從而避免資料不一致和併發衝突。本文將詳細介紹Java併發程式設計中的鎖機制,包括內建鎖、重入鎖、讀寫鎖等,並透過程式碼示例講解其使用方法和原理。

1. 內建鎖(synchronized)

Java的內建鎖是透過synchronized關鍵字實現的,synchronized可以用來修飾方法或者程式碼塊,確保同一時刻只有一個執行緒能夠執行被synchronized保護的程式碼。

package cn.juwatech.lock;

public class SynchronizedDemo {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }

    public static void main(String[] args) throws InterruptedException {
        SynchronizedDemo demo = new SynchronizedDemo();

        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                demo.increment();
            }
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("Final count: " + demo.getCount());
    }
}

在上述程式碼中,increment方法被synchronized修飾,保證了count變數的自增操作是執行緒安全的。

2. 重入鎖(ReentrantLock)

ReentrantLock是Java併發包中的可重入鎖,它提供了比synchronized更靈活的鎖機制。例如,ReentrantLock可以實現公平鎖、非阻塞地獲取鎖等功能。

package cn.juwatech.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo {
    private int count = 0;
    private final Lock lock = new ReentrantLock();

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    public int getCount() {
        return count;
    }

    public static void main(String[] args) throws InterruptedException {
        ReentrantLockDemo demo = new ReentrantLockDemo();

        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                demo.increment();
            }
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("Final count: " + demo.getCount());
    }
}

上述程式碼中,increment方法使用ReentrantLock進行加鎖和解鎖,確保count變數的自增操作是執行緒安全的。

3. 讀寫鎖(ReadWriteLock)

ReadWriteLock是另一種常用的鎖機制,它允許多個讀執行緒併發地訪問,但寫執行緒是獨佔的。這在讀多寫少的場景中可以顯著提高併發效能。

package cn.juwatech.lock;

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockDemo {
    private int count = 0;
    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();

    public void increment() {
        rwLock.writeLock().lock();
        try {
            count++;
        } finally {
            rwLock.writeLock().unlock();
        }
    }

    public int getCount() {
        rwLock.readLock().lock();
        try {
            return count;
        } finally {
            rwLock.readLock().unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ReadWriteLockDemo demo = new ReadWriteLockDemo();

        Runnable writeTask = () -> {
            for (int i = 0; i < 1000; i++) {
                demo.increment();
            }
        };

        Runnable readTask = () -> {
            for (int i = 0; i < 1000; i++) {
                System.out.println("Count: " + demo.getCount());
            }
        };

        Thread t1 = new Thread(writeTask);
        Thread t2 = new Thread(readTask);
        Thread t3 = new Thread(readTask);

        t1.start();
        t2.start();
        t3.start();

        t1.join();
        t2.join();
        t3.join();

        System.out.println("Final count: " + demo.getCount());
    }
}

在上述程式碼中,increment方法使用寫鎖進行加鎖,getCount方法使用讀鎖進行加鎖,確保讀寫操作的執行緒安全。

4. 鎖的條件變數

ReentrantLock還提供了條件變數(Condition),可以實現更復雜的執行緒同步控制。透過Condition物件,可以讓執行緒在某個條件下等待,直到條件滿足後再繼續執行。

package cn.juwatech.lock;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionDemo {
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    private boolean ready = false;

    public void waitForCondition() {
        lock.lock();
        try {
            while (!ready) {
                condition.await();
            }
            System.out.println("Condition met, proceeding...");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock();
        }
    }

    public void signalCondition() {
        lock.lock();
        try {
            ready = true;
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ConditionDemo demo = new ConditionDemo();

        Runnable waitingTask = demo::waitForCondition;
        Runnable signalingTask = demo::signalCondition;

        Thread t1 = new Thread(waitingTask);
        Thread t2 = new Thread(signalingTask);

        t1.start();
        Thread.sleep(1000);
        t2.start();

        t1.join();
        t2.join();
    }
}

在上述程式碼中,waitForCondition方法在條件不滿足時等待,直到signalCondition方法將條件置為滿足並通知所有等待執行緒。

5. StampedLock

StampedLock是Java 8引入的一種鎖機制,它提供了更高效的讀寫鎖,並且支援樂觀讀操作。StampedLock的主要特點是它使用了戳記(stamp)來控制鎖的狀態。

package cn.juwatech.lock;

import java.util.concurrent.locks.StampedLock;

public class StampedLockDemo {
    private int count = 0;
    private final StampedLock stampedLock = new StampedLock();

    public void increment() {
        long stamp = stampedLock.writeLock();
        try {
            count++;
        } finally {
            stampedLock.unlockWrite(stamp);
        }
    }

    public int getCount() {
        long stamp = stampedLock.tryOptimisticRead();
        int currentCount = count;
        if (!stampedLock.validate(stamp)) {
            stamp = stampedLock.readLock();
            try {
                currentCount = count;
            } finally {
                stampedLock.unlockRead(stamp);
            }
        }
        return currentCount;
    }

    public static void main(String[] args) throws InterruptedException {
        StampedLockDemo demo = new StampedLockDemo();

        Runnable writeTask = () -> {
            for (int i = 0; i < 1000; i++) {
                demo.increment();
            }
        };

        Runnable readTask = () -> {
            for (int i = 0; i < 1000; i++) {
                System.out.println("Count: " + demo.getCount());
            }
        };

        Thread t1 = new Thread(writeTask);
        Thread t2 = new Thread(readTask);
        Thread t3 = new Thread(readTask);

        t1.start();
        t2.start();
        t3.start();

        t1.join();
        t2.join();
        t3.join();

        System.out.println("Final count: " + demo.getCount());
    }
}

在上述程式碼中,increment方法使用寫鎖進行加鎖,getCount方法嘗試使用樂觀讀鎖,如果失敗則降級為悲觀讀鎖,確保讀寫操作的執行緒安全。

總結

本文詳細介紹了Java併發程式設計中的鎖機制,包括內建鎖、重入鎖、讀寫鎖、條件變數和StampedLock。透過這些示例程式碼,開發者可以更好地理解和使用Java的各種鎖機制,提高併發程式的效能和可靠性。

著作權歸聚娃科技微賺淘客系統開發者團隊,轉載請註明出處!

相關文章