亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java中的內(nèi)存模型(JMM)和鎖機(jī)制詳解

 更新時(shí)間:2025年06月10日 10:53:45   作者:紂王家子迎新  
這篇文章主要介紹了Java中的內(nèi)存模型(JMM)和鎖機(jī)制使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

Java內(nèi)存模型(Java Memory Model, JMM)是Java虛擬機(jī)(JVM)規(guī)范中定義的一種內(nèi)存模型(抽象概念),它描述了Java程序中各種變量(包括實(shí)例字段、靜態(tài)字段和構(gòu)成數(shù)組對(duì)象的元素)的訪問規(guī)則,以及在多線程環(huán)境下這些變量如何被線程共享和同步(變量的共享和同步機(jī)制)。

JMM的主要目標(biāo)是//定義線程如何以及何時(shí)可以看到由其他線程修改過的共享變量的值,以及在必須時(shí)進(jìn)行同步的機(jī)制和方式//,//既定義線程如何與主內(nèi)存進(jìn)行交互,以及如何保證原子性、可見性和有序性//。它幫助程序員編寫出在多線程環(huán)境下能夠正確運(yùn)行的Java程序。

Java內(nèi)存模型(JMM)

Java內(nèi)存模型定義了線程和主內(nèi)存之間的抽象關(guān)系,以及線程之間如何共享變量。它主要解決了多線程環(huán)境下,變量可見性和有序性的問題。

  • ‌變量可見性‌:當(dāng)一個(gè)線程修改了共享變量的值,其他線程能夠立即得知這個(gè)修改。在Java中,這通常通過volatile關(guān)鍵字或synchronized塊來(lái)保證。
  • ‌有序性‌:JMM允許編譯器和處理器對(duì)指令進(jìn)行重排序以優(yōu)化性能,但這可能會(huì)導(dǎo)致多線程程序出現(xiàn)意外的行為。通過volatile關(guān)鍵字或synchronized塊可以禁止這種重排序。

JMM規(guī)定了所有變量都存儲(chǔ)在主內(nèi)存中,每個(gè)線程還有自己的工作內(nèi)存(線程棧的一部分),線程對(duì)變量的所有操作都必須在工作內(nèi)存中完成,然后再刷新到主內(nèi)存。

JMM定義的關(guān)鍵概念

  • ‌主內(nèi)存:Java堆內(nèi)存中的方法區(qū)以及所有Java線程共享的內(nèi)存區(qū)域。所有變量都存儲(chǔ)在主內(nèi)存中,包括實(shí)例變量、靜態(tài)變量和數(shù)組元素,。
  • ‌工作內(nèi)存:每個(gè)線程都有自己的工作內(nèi)存(也稱為本地內(nèi)存),它是線程私有的,包含了該線程對(duì)共享變量的私有副本以及線程執(zhí)行所需的其他信息(如指令、常量等)。線程對(duì)共享變量的所有操作(讀取、賦值等)都必須在工作內(nèi)存中進(jìn)行(不能直接讀寫主內(nèi)存中的變量),然后再刷新回主內(nèi)存。
  • ‌可見性:一個(gè)線程修改了共享變量的值,其他線程能夠立即得知這個(gè)修改。JMM通過規(guī)定線程在何時(shí)必須將工作內(nèi)存中的變量值刷新回主內(nèi)存,以及何時(shí)必須從主內(nèi)存中重新讀取共享變量的值,來(lái)實(shí)現(xiàn)可見性。(大概就是:不同線程之間無(wú)法直接訪問對(duì)方工作內(nèi)存中的變量,線程間的變量值的傳遞均需要通過主內(nèi)存來(lái)完成,當(dāng)一個(gè)線程修改了共享變量的值,其他線程能夠立即得知這個(gè)修改)
  • ‌原子性:一個(gè)或多個(gè)操作在執(zhí)行過程中不會(huì)被其他線程中斷。Java內(nèi)存模型只保證基本數(shù)據(jù)類型的讀取和賦值是原子的,對(duì)于復(fù)合操作(如i++),則需要通過同步機(jī)制來(lái)保證原子性。(大概就是:一個(gè)操作要么全部完成,要么完全不發(fā)生,不會(huì)被線程調(diào)度機(jī)制中斷)
  • ‌有序性:程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。但是,在并發(fā)環(huán)境下,由于指令重排序(Instruction Reorder)的存在,程序的執(zhí)行順序可能與代碼順序不一致。Java內(nèi)存模型允許編譯器和處理器對(duì)指令進(jìn)行重排序,以提高程序執(zhí)行的性能。但是,重排序過程不會(huì)違反as-if-serial語(yǔ)義,即程序執(zhí)行的結(jié)果應(yīng)該與在單線程中按代碼順序執(zhí)行的結(jié)果一致。(大概就是:程序執(zhí)行的順序按照代碼的先后順序執(zhí)行,但在多線程環(huán)境中,由于指令重排序等優(yōu)化手段,實(shí)際的執(zhí)行順序可能與代碼順序不一致)

Java提供的同步機(jī)制

為了保證多線程程序的正確性,Java提供了多種同步機(jī)制,如synchronized關(guān)鍵字、volatile關(guān)鍵字、Lock接口等,這些機(jī)制可以幫助程序員控制線程之間的內(nèi)存可見性和操作的有序性。

主要的同步機(jī)制

‌synchronized關(guān)鍵字‌

這是Java最基本的同步機(jī)制。它可以用來(lái)修飾方法或代碼塊,確保在同一時(shí)刻只有一個(gè)線程能夠執(zhí)行該方法或代碼塊。它通過鎖機(jī)制(通常是對(duì)象鎖或類鎖)來(lái)確保同一時(shí)刻只有一個(gè)線程能執(zhí)行某個(gè)方法或代碼塊。

修飾方法時(shí),它會(huì)自動(dòng)鎖定調(diào)用該方法的對(duì)象或該類(如果是靜態(tài)方法)。

修飾代碼塊時(shí),需要指定一個(gè)鎖對(duì)象,多個(gè)線程需要對(duì)該鎖對(duì)象進(jìn)行競(jìng)爭(zhēng),以獲得執(zhí)行權(quán)限。

使用synchronized關(guān)鍵字的示例

public class Counter {
    private int count = 0;

    // 使用synchronized修飾方法,確保線程安全
    public synchronized void increment() {
        count++; // 這是一個(gè)非原子操作,但在synchronized方法中它是安全的
    }

    public synchronized int getCount() {
        return count;
    }

    public static void main(String[] args) throws InterruptedException {
        final Counter counter = new Counter();

        // 創(chuàng)建兩個(gè)線程來(lái)更新計(jì)數(shù)器
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                counter.increment();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                counter.increment();
            }
        });

        // 啟動(dòng)線程
        t1.start();
        t2.start();

        // 等待線程完成
        t1.join();
        t2.join();

        // 打印最終計(jì)數(shù)器值
        System.out.println("Final count: " + counter.getCount()); // 應(yīng)該是20000
    }
}
‌volatile關(guān)鍵字‌

用于確保變量的可見性。當(dāng)一個(gè)變量被volatile修飾后,它會(huì)告訴JVM該變量的值可能會(huì)被其他線程修改,因此在每次讀取該變量時(shí)都需要重新從主內(nèi)存中讀取,而不是從線程的工作內(nèi)存中讀取。

注意,volatile不能確保操作的原子性,它僅適用于那些不需要同步代碼塊就能保證原子性的操作。

使用volatile關(guān)鍵字的示例

public class VolatileExample {
    // 使用volatile修飾共享變量,確保所有線程都能看到最新的值
    private volatile boolean running = true;

    // 啟動(dòng)一個(gè)線程來(lái)模擬運(yùn)行中的任務(wù)
    public void startTask() {
        Thread taskThread = new Thread(() -> {
            while (running) {
                // 模擬任務(wù)執(zhí)行,這里只是簡(jiǎn)單地打印信息
                System.out.println("Task is running...");
                try {
                    // 為了演示,讓線程暫停一段時(shí)間
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt(); // 保持中斷狀態(tài)
                    return;
                }
            }
            System.out.println("Task has stopped.");
        });

        // 啟動(dòng)線程
        taskThread.start();

        // 假設(shè)在一段時(shí)間后,我們想要停止任務(wù)
        try {
            Thread.sleep(5000); // 等待5秒來(lái)模擬任務(wù)運(yùn)行時(shí)間
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return;
        }

        // 更新volatile變量以停止任務(wù)
        running = false;
    }

    public static void main(String[] args) {
        VolatileExample example = new VolatileExample();
        example.startTask();

        // 注意:在實(shí)際應(yīng)用中,你可能需要等待taskThread真正結(jié)束,這里為了簡(jiǎn)化示例而省略了
    }
}

在這個(gè)示例中,running變量被volatile修飾,這意味著當(dāng)running的值被修改時(shí),所有線程都會(huì)看到最新的值。在startTask方法中,我們啟動(dòng)了一個(gè)線程來(lái)模擬一個(gè)長(zhǎng)時(shí)間運(yùn)行的任務(wù),該任務(wù)通過檢查running變量的值來(lái)決定是否繼續(xù)執(zhí)行。在主線程中,我們通過將running設(shè)置為false來(lái)停止任務(wù)。由于running是volatile的,因此當(dāng)它被更新時(shí),運(yùn)行任務(wù)的線程能夠立即看到這一變化,并相應(yīng)地停止執(zhí)行。

‌wait()和notify()/notifyAll()方法‌

這三個(gè)方法都是Object類中的方法,用于線程間的通信。

wait()方法會(huì)使當(dāng)前線程等待,直到其他線程調(diào)用該對(duì)象的notify()或notifyAll()方法。調(diào)用wait()方法的線程必須持有該對(duì)象的鎖,調(diào)用后該線程會(huì)釋放鎖并進(jìn)入等待狀態(tài)。

notify()方法會(huì)喚醒等待該對(duì)象鎖的單個(gè)線程(如果有的話),而notifyAll()會(huì)喚醒所有等待該對(duì)象鎖的線程。

使用wait()和notify()/notifyAll()方法‌的示例

public class WaitNotifyExample {
    private final Object lock = new Object(); // 鎖對(duì)象
    private boolean ready = false; // 條件變量

    // 生產(chǎn)者線程調(diào)用此方法
    public void produce() throws InterruptedException {
        synchronized (lock) {
            // 假設(shè)生產(chǎn)者需要做一些準(zhǔn)備工作
            System.out.println("Producer is preparing...");
            Thread.sleep(1000); // 模擬耗時(shí)操作

            // 準(zhǔn)備工作完成,設(shè)置條件變量并通知等待的消費(fèi)者
            ready = true;
            lock.notify(); // 喚醒等待在該鎖上的一個(gè)線程
            System.out.println("Product is ready. Notified consumer.");
        }
    }

    // 消費(fèi)者線程調(diào)用此方法
    public void consume() throws InterruptedException {
        synchronized (lock) {
            // 等待產(chǎn)品準(zhǔn)備好
            while (!ready) {
                lock.wait(); // 釋放鎖并進(jìn)入等待狀態(tài)
            }

            // 產(chǎn)品已準(zhǔn)備好,開始消費(fèi)
            System.out.println("Consumer is consuming the product.");
            Thread.sleep(1000); // 模擬消費(fèi)過程

            // 消費(fèi)完成,重置條件變量供下一輪使用
            ready = false;
        }
    }

    public static void main(String[] args) {
        WaitNotifyExample example = new WaitNotifyExample();

        // 創(chuàng)建生產(chǎn)者線程
        Thread producer = new Thread(() -> {
            try {
                example.produce();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        // 創(chuàng)建消費(fèi)者線程
        Thread consumer = new Thread(() -> {
            try {
                example.consume();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        // 啟動(dòng)線程
        producer.start();
        consumer.start();

        // 注意:在這個(gè)簡(jiǎn)單的示例中,沒有等待消費(fèi)者線程完成。
        // 在實(shí)際應(yīng)用中,你可能需要某種形式的同步來(lái)確保生產(chǎn)者和消費(fèi)者之間的正確交互。
    }
}

請(qǐng)注意,在這個(gè)示例中,wait() 方法被放在了一個(gè) while 循環(huán)中。這是因?yàn)?wait() 方法可能會(huì)由于“虛假喚醒”(spurious wakeup)而被喚醒,即使沒有線程調(diào)用 notify() 或 notifyAll()。因此,將 wait() 放在 while 循環(huán)中并使用條件變量來(lái)檢查實(shí)際狀態(tài)是一種常見且推薦的做法。

此外,還需要注意,wait(), notify(), 和 notifyAll() 方法必須在同步代碼塊或同步方法中調(diào)用,因?yàn)樗鼈円蕾囉趯?duì)象鎖。在這個(gè)示例中,我們使用了一個(gè)單獨(dú)的鎖對(duì)象 lock 來(lái)控制對(duì)共享資源(這里是 ready 變量)的訪問和線程間的通信。

‌Lock接口‌

Java 5.0引入了java.util.concurrent.locks包,其中定義了Lock接口。Lock接口提供了比synchronized關(guān)鍵字更靈活的鎖定機(jī)制。

Lock接口的實(shí)現(xiàn)類(如ReentrantLock,它支持可重入的互斥鎖,還支持嘗試非阻塞地獲取鎖、可中斷地獲取鎖、定時(shí)嘗試獲取鎖等功能)提供了更加豐富的功能,如嘗試非阻塞地獲取鎖、可中斷地獲取鎖、定時(shí)嘗試獲取鎖等。

使用ReentrantLock的示例

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

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

    public void increment() {
        lock.lock(); // 獲取鎖
        try {
            count++;
        } finally {
            lock.unlock(); // 釋放鎖,無(wú)論是否發(fā)生異常
        }
    }

    public int getCount() {
        lock.lock(); // 這里也可以考慮使用tryLock()或讀寫鎖等更細(xì)粒度的控制
        try {
            return count;
        } finally {
            lock.unlock();
        }
    }

    // main方法和上面的示例類似,只是Counter類被替換為了CounterWithLock類
}
‌Condition接口‌

Condition接口是與Lock接口配合使用的,是Lock接口提供的一個(gè)條件對(duì)象,用于線程間的通信,它提供了更靈活的線程間通信方式。

每個(gè)Lock對(duì)象都可以創(chuàng)建多個(gè)Condition實(shí)例,用于實(shí)現(xiàn)更復(fù)雜的線程間通信。

使用Condition接口‌示例

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

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

    // 線程執(zhí)行的任務(wù)
    public void task() throws InterruptedException {
        lock.lock(); // 獲取鎖
        try {
            // 等待條件滿足
            while (!ready) {
                condition.await(); // 釋放鎖并進(jìn)入等待狀態(tài)
            }

            // 條件滿足,執(zhí)行任務(wù)
            System.out.println("Condition met, task is executing.");

            // 假設(shè)任務(wù)完成后需要重置條件
            ready = false;
        } finally {
            lock.unlock(); // 無(wú)論如何,最后都要釋放鎖
        }
    }

    // 觸發(fā)條件的方法
    public void triggerCondition() {
        lock.lock(); // 獲取鎖
        try {
            ready = true; // 設(shè)置條件為true
            condition.signalAll(); // 喚醒所有等待該條件的線程
        } finally {
            lock.unlock(); // 釋放鎖
        }
    }

    public static void main(String[] args) {
        ConditionExample example = new ConditionExample();

        // 創(chuàng)建并啟動(dòng)線程
        Thread thread = new Thread(() -> {
            try {
                example.task();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        thread.start();

        // 主線程等待一會(huì)兒,然后觸發(fā)條件
        try {
            Thread.sleep(1000); // 等待一秒,模擬其他操作
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        example.triggerCondition(); // 觸發(fā)條件,喚醒等待的線程
    }
}

在這個(gè)示例中,我們定義了一個(gè)ConditionExample類,它包含一個(gè)ReentrantLock鎖和一個(gè)Condition條件對(duì)象。task()方法模擬了一個(gè)線程需要等待某個(gè)條件滿足才能執(zhí)行的任務(wù),而triggerCondition()方法則用于在滿足某個(gè)條件時(shí)觸發(fā)該條件,從而喚醒等待的線程。

注意,在調(diào)用condition.await()時(shí),線程會(huì)釋放鎖并進(jìn)入等待狀態(tài),直到其他線程調(diào)用condition.signal()或condition.signalAll()來(lái)喚醒它。同樣地,在調(diào)用這些方法之前,必須持有與Condition對(duì)象相關(guān)聯(lián)的鎖。這是通過調(diào)用lock.lock()來(lái)完成的,并且在方法結(jié)束時(shí)通過lock.unlock()來(lái)釋放鎖,以確保鎖的釋放總是會(huì)發(fā)生,無(wú)論方法是否成功執(zhí)行。

‌Semaphore(信號(hào)量)‌

Semaphore是一個(gè)計(jì)數(shù)信號(hào)量,是一種基于計(jì)數(shù)的同步機(jī)制,可以用來(lái)控制同時(shí)訪問某個(gè)特定資源的線程數(shù)量。它不是直接通過某個(gè)接口或類的形式實(shí)現(xiàn)的,而是java.util.concurrent包中的一個(gè)類。

它允許多個(gè)線程同時(shí)訪問某個(gè)資源,但會(huì)限制同時(shí)訪問的線程數(shù)。

使用Semaphore(信號(hào)量)的示例

import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    // 創(chuàng)建一個(gè)Semaphore對(duì)象,設(shè)置許可數(shù)量為2
    // 這意味著同時(shí)最多只能有兩個(gè)線程可以訪問資源
    private final Semaphore semaphore = new Semaphore(2);

    // 模擬的資源訪問方法
    public void accessResource() {
        try {
            // 請(qǐng)求許可
            semaphore.acquire();
            System.out.println(Thread.currentThread().getName() + " has acquired a permit and is accessing the resource.");

            // 模擬資源訪問的耗時(shí)操作
            Thread.sleep(1000);

            // 訪問完成后,釋放許可
            semaphore.release();
            System.out.println(Thread.currentThread().getName() + " has released the permit.");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // 保持中斷狀態(tài)
            System.out.println(Thread.currentThread().getName() + " was interrupted while accessing the resource.");
        }
    }

    public static void main(String[] args) {
        SemaphoreExample example = new SemaphoreExample();

        // 創(chuàng)建并啟動(dòng)多個(gè)線程來(lái)訪問資源
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                example.accessResource();
            }, "Thread-" + (i + 1)).start();
        }
    }
}

在這個(gè)示例中,我們創(chuàng)建了一個(gè)Semaphore對(duì)象,其初始許可數(shù)量為2。然后,我們定義了一個(gè)accessResource方法來(lái)模擬對(duì)資源的訪問。在訪問資源之前,線程會(huì)嘗試通過調(diào)用semaphore.acquire()來(lái)獲取許可。由于許可數(shù)量被限制為2,因此同時(shí)最多只有兩個(gè)線程能夠進(jìn)入accessResource方法的主體部分。一旦線程完成了對(duì)資源的訪問,它就會(huì)通過調(diào)用semaphore.release()來(lái)釋放許可,從而允許其他等待的線程獲取許可并訪問資源。

運(yùn)行這個(gè)程序,你會(huì)看到類似以下的輸出(具體順序可能會(huì)有所不同,因?yàn)榫€程的執(zhí)行是并發(fā)的):

Thread-1 has acquired a permit and is accessing the resource.

Thread-2 has acquired a permit and is accessing the resource.

Thread-1 has released the permit.

Thread-3 has acquired a permit and is accessing the resource.

Thread-2 has released the permit.

Thread-4 has acquired a permit and is accessing the resource.

Thread-3 has released the permit.

Thread-5 has acquired a permit and is accessing the resource.

Thread-4 has released the permit. Thread-5 has released the permit.

‌CountDownLatch(倒計(jì)時(shí)鎖存器)‌

CountDownLatch是一種同步輔助類,它允許一個(gè)或多個(gè)線程等待直到在其他線程中執(zhí)行的一組操作完成。它也不是通過接口或繼承關(guān)系實(shí)現(xiàn)的,而是直接作為一個(gè)類存在。

它通常用于等待直到一組異步操作完成。

使用CountDownLatch(倒計(jì)時(shí)鎖存器)‌的示例

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CountDownLatchExample {

    public static void main(String[] args) throws InterruptedException {
        // 創(chuàng)建一個(gè)CountDownLatch實(shí)例,設(shè)置計(jì)數(shù)器的初始值為2
        // 這意味著我們需要等待兩個(gè)任務(wù)完成
        CountDownLatch latch = new CountDownLatch(2);

        // 創(chuàng)建一個(gè)線程池來(lái)執(zhí)行任務(wù)
        ExecutorService executor = Executors.newFixedThreadPool(2);

        // 提交第一個(gè)任務(wù)
        executor.submit(() -> {
            try {
                // 模擬任務(wù)執(zhí)行
                System.out.println("Task 1 is running");
                Thread.sleep(1000); // 假設(shè)任務(wù)需要1秒來(lái)完成
                System.out.println("Task 1 is done");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                // 任務(wù)完成后,計(jì)數(shù)器的值減1
                latch.countDown();
            }
        });

        // 提交第二個(gè)任務(wù)
        executor.submit(() -> {
            try {
                // 模擬任務(wù)執(zhí)行
                System.out.println("Task 2 is running");
                Thread.sleep(2000); // 假設(shè)這個(gè)任務(wù)需要2秒來(lái)完成
                System.out.println("Task 2 is done");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                // 任務(wù)完成后,計(jì)數(shù)器的值減1
                latch.countDown();
            }
        });

        // 等待直到所有任務(wù)完成
        latch.await(); // 這會(huì)阻塞當(dāng)前線程,直到計(jì)數(shù)器的值變?yōu)?

        // 關(guān)閉線程池
        executor.shutdown();

        // 所有任務(wù)都已完成,繼續(xù)執(zhí)行后續(xù)操作
        System.out.println("All tasks are completed.");
    }
}

在這個(gè)示例中,我們創(chuàng)建了一個(gè)CountDownLatch實(shí)例,其計(jì)數(shù)器的初始值為2。然后,我們提交了兩個(gè)任務(wù)到線程池中執(zhí)行。每個(gè)任務(wù)在執(zhí)行完成后都會(huì)調(diào)用latch.countDown()來(lái)將計(jì)數(shù)器的值減1。主線程通過調(diào)用latch.await()來(lái)等待,直到計(jì)數(shù)器的值變?yōu)?,這表示所有任務(wù)都已經(jīng)完成。然后,主線程可以繼續(xù)執(zhí)行后續(xù)操作。

‌CyclicBarrier(循環(huán)屏障)‌

CyclicBarrier是一種同步輔助類,它用于讓一組線程相互等待,直到到達(dá)某個(gè)公共屏障點(diǎn)。類似于CountDownLatch,CyclicBarrier也是直接作為一個(gè)類存在,而不是通過接口或繼承關(guān)系實(shí)現(xiàn)的。

在所有線程都到達(dá)屏障點(diǎn)之前,它們將在屏障點(diǎn)處阻塞。當(dāng)最后一個(gè)線程到達(dá)屏障點(diǎn)時(shí),屏障會(huì)打開,此時(shí)所有線程都將被釋放并繼續(xù)執(zhí)行。

使用CyclicBarrier(循環(huán)屏障)‌的示例

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {

    // 創(chuàng)建一個(gè)CyclicBarrier對(duì)象,設(shè)置屏障點(diǎn)需要等待的線程數(shù)量為3
    // 第二個(gè)參數(shù)是一個(gè)Runnable對(duì)象,它是當(dāng)所有線程都到達(dá)屏障點(diǎn)時(shí)會(huì)執(zhí)行的任務(wù)(可選)
    private final CyclicBarrier barrier = new CyclicBarrier(3, () -> {
        System.out.println("All threads have reached the barrier. Barrier is now broken.");
    });

    public void task() {
        // 模擬任務(wù)執(zhí)行前的準(zhǔn)備工作
        try {
            // 假設(shè)每個(gè)線程都需要一些時(shí)間來(lái)準(zhǔn)備
            Thread.sleep((long) (Math.random() * 1000));
            System.out.println(Thread.currentThread().getName() + " is ready.");

            // 等待直到所有線程都到達(dá)屏障點(diǎn)
            barrier.await();

            // 所有線程都到達(dá)屏障點(diǎn)后,繼續(xù)執(zhí)行后續(xù)任務(wù)
            System.out.println(Thread.currentThread().getName() + " is continuing after the barrier.");
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        CyclicBarrierExample example = new CyclicBarrierExample();

        // 創(chuàng)建并啟動(dòng)三個(gè)線程來(lái)執(zhí)行任務(wù)
        for (int i = 1; i <= 3; i++) {
            new Thread(() -> {
                example.task();
            }, "Thread-" + i).start();
        }
    }
}

在這個(gè)示例中,我們創(chuàng)建了一個(gè)CyclicBarrier對(duì)象,其參數(shù)為3,表示需要等待3個(gè)線程都到達(dá)屏障點(diǎn)。我們還提供了一個(gè)Runnable對(duì)象作為可選參數(shù),它將在所有線程都到達(dá)屏障點(diǎn)時(shí)執(zhí)行。

然后,我們定義了一個(gè)task()方法,該方法模擬了線程在到達(dá)屏障點(diǎn)之前的準(zhǔn)備工作,并調(diào)用了barrier.await()來(lái)等待其他線程。一旦所有線程都調(diào)用了await()方法并成功到達(dá)屏障點(diǎn),屏障就會(huì)被打開,所有線程都會(huì)繼續(xù)執(zhí)行await()之后的代碼。

在main()方法中,我們創(chuàng)建了三個(gè)線程來(lái)執(zhí)行task()方法,并啟動(dòng)了它們。你會(huì)注意到,每個(gè)線程都會(huì)打印一條消息表示它已準(zhǔn)備好,然后等待其他線程。一旦所有線程都到達(dá)屏障點(diǎn),它們會(huì)一起繼續(xù)執(zhí)行后續(xù)任務(wù),并打印相應(yīng)的消息。

這個(gè)示例展示了CyclicBarrier如何用于同步一組線程的執(zhí)行,直到它們都到達(dá)某個(gè)公共點(diǎn)。

鎖機(jī)制

鎖機(jī)制是計(jì)算機(jī)編程中一種用于控制對(duì)共享資源訪問的重要同步機(jī)制。在并發(fā)編程中,多個(gè)線程或進(jìn)程可能會(huì)同時(shí)嘗試訪問同一資源,這就有可能導(dǎo)致數(shù)據(jù)不一致、競(jìng)態(tài)條件等問題。鎖機(jī)制通過確保在同一時(shí)間只有一個(gè)線程或進(jìn)程能夠訪問某個(gè)資源,從而避免了這些問題。Java中主要有兩種鎖機(jī)制:

‌內(nèi)置鎖(Synchronized Locks)‌

通過synchronized關(guān)鍵字實(shí)現(xiàn),可以修飾方法或代碼塊。當(dāng)一個(gè)線程訪問某個(gè)對(duì)象的synchronized方法或代碼塊時(shí),它會(huì)先嘗試獲取該對(duì)象的鎖;如果鎖已被其他線程持有,則該線程會(huì)阻塞,直到鎖被釋放。

‌顯式鎖(Explicit Locks)‌

從Java 1.5開始,引入了java.util.concurrent.locks包,提供了比synchronized更靈活的鎖機(jī)制,如ReentrantLock。顯式鎖允許更復(fù)雜的同步控制,如嘗試非阻塞地獲取鎖、可中斷地獲取鎖以及嘗試獲取鎖時(shí)設(shè)置超時(shí)等。

Java中提供了多種鎖機(jī)制,例如:

  • ‌synchronized關(guān)鍵字‌:Java內(nèi)置的同步機(jī)制,可以修飾方法或代碼塊。當(dāng)一個(gè)線程訪問某個(gè)對(duì)象的synchronized方法或代碼塊時(shí),它會(huì)獲得該對(duì)象的鎖,其他線程必須等待鎖被釋放后才能訪問。
  • ‌ReentrantLock‌:這是java.util.concurrent.locks包下的一個(gè)類,提供了比synchronized更靈活的鎖定操作。它支持顯式地鎖定和解鎖操作,也支持嘗試鎖定、定時(shí)鎖定和可中斷的鎖定。
  • ‌ReadWriteLock‌:讀寫鎖,它允許多個(gè)讀線程同時(shí)訪問資源,但在寫線程訪問資源時(shí),會(huì)排斥其他所有讀線程和寫線程。這對(duì)于讀操作遠(yuǎn)多于寫操作的場(chǎng)景非常有用,可以顯著提高性能。
  • ‌StampedLock‌:Java 8中引入的一種鎖,它提供了對(duì)讀寫鎖的更優(yōu)支持,以及一種樂觀讀鎖的模式。與ReadWriteLock相比,StampedLock在某些情況下可以提供更高的吞吐量。

鎖機(jī)制的選擇取決于具體的應(yīng)用場(chǎng)景和性能要求。例如,在需要簡(jiǎn)單且自動(dòng)管理的鎖時(shí),可以使用synchronized;在需要更靈活的控制時(shí),可以使用ReentrantLock;在讀多寫少的場(chǎng)景下,ReadWriteLock或StampedLock可能是更好的選擇。

總的來(lái)說(shuō),鎖機(jī)制是并發(fā)編程中不可或缺的一部分,它們幫助程序員管理對(duì)共享資源的訪問,從而避免了并發(fā)編程中常見的問題。

JMM和鎖機(jī)制的關(guān)系

JMM和鎖機(jī)制在Java并發(fā)編程中相互協(xié)作,共同確保多線程程序的正確性和高效性。JMM定義了線程間如何共享和訪問變量,而鎖機(jī)制則提供了一種同步機(jī)制,確保在并發(fā)環(huán)境下對(duì)共享資源的訪問是安全的。

  • ‌變量可見性‌:通過鎖機(jī)制(如synchronized塊或volatile變量)可以確保一個(gè)線程對(duì)共享變量的修改能夠被其他線程看到。
  • ‌互斥訪問‌:鎖機(jī)制通過互斥地訪問共享資源來(lái)防止數(shù)據(jù)競(jìng)爭(zhēng)和不一致性的發(fā)生。

總的來(lái)說(shuō)

Java內(nèi)存模型和鎖機(jī)制是Java并發(fā)編程的基石,理解和掌握它們對(duì)于編寫高效、可維護(hù)的并發(fā)程序至關(guān)重要。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java空指針異常NullPointerException的原因與解決方案

    Java空指針異常NullPointerException的原因與解決方案

    在Java開發(fā)中,NullPointerException(空指針異常)是最常見的運(yùn)行時(shí)異常之一,通常發(fā)生在程序嘗試訪問或操作一個(gè)為null的對(duì)象引用時(shí),這種異常不僅會(huì)導(dǎo)致程序崩潰,還會(huì)增加調(diào)試難度,所以本文系統(tǒng)梳理NullPointerException的成因、調(diào)試方法和避免策略
    2025-06-06
  • java ExecutorService使用方法詳解

    java ExecutorService使用方法詳解

    這篇文章主要為大家詳細(xì)介紹了java ExecutorService使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • 基于Protobuf動(dòng)態(tài)解析在Java中的應(yīng)用 包含例子程序

    基于Protobuf動(dòng)態(tài)解析在Java中的應(yīng)用 包含例子程序

    下面小編就為大家?guī)?lái)一篇基于Protobuf動(dòng)態(tài)解析在Java中的應(yīng)用 包含例子程序。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧
    2017-07-07
  • SpringBoot2零基礎(chǔ)到精通之映射與常用注解請(qǐng)求處理

    SpringBoot2零基礎(chǔ)到精通之映射與常用注解請(qǐng)求處理

    SpringBoot是一種整合Spring技術(shù)棧的方式(或者說(shuō)是框架),同時(shí)也是簡(jiǎn)化Spring的一種快速開發(fā)的腳手架,本篇讓我們一起學(xué)習(xí)映射、常用注解和方法參數(shù)的小技巧
    2022-03-03
  • Java緩沖字符流的實(shí)現(xiàn)示例

    Java緩沖字符流的實(shí)現(xiàn)示例

    本文詳細(xì)介紹了Java緩沖字符流的使用,通過使用BufferedReader和BufferedWriter,可以更高效地處理文本數(shù)據(jù),提高讀寫效率,感興趣的可以了解一下
    2024-12-12
  • SpringCloud Feign遠(yuǎn)程調(diào)用實(shí)現(xiàn)詳解

    SpringCloud Feign遠(yuǎn)程調(diào)用實(shí)現(xiàn)詳解

    Feign是Netflix公司開發(fā)的一個(gè)聲明式的REST調(diào)用客戶端; Ribbon負(fù)載均衡、 Hystrⅸ服務(wù)熔斷是我們Spring Cloud中進(jìn)行微服務(wù)開發(fā)非?;A(chǔ)的組件,在使用的過程中我們也發(fā)現(xiàn)它們一般都是同時(shí)出現(xiàn)的,而且配置也都非常相似
    2022-11-11
  • 關(guān)于線程池你不得不知道的一些設(shè)置

    關(guān)于線程池你不得不知道的一些設(shè)置

    這篇文章主要介紹了關(guān)于線程池你不得不知道的一些設(shè)置,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧<BR>
    2019-04-04
  • dubbo入門指南及demo實(shí)例詳解

    dubbo入門指南及demo實(shí)例詳解

    Dubbo是一個(gè)分布式服務(wù)框架,致力于提供高性能和透明化的RPC遠(yuǎn)程服務(wù)調(diào)用方案,以及SOA服務(wù)治理方案,這篇文章主要介紹了dubbo詳解及demo實(shí)例,需要的朋友可以參考下
    2022-04-04
  • SpringBoot中讀取application.properties配置文件的方法

    SpringBoot中讀取application.properties配置文件的方法

    這篇文章主要介紹了SpringBoot中讀取application.properties配置文件的三種方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2023-02-02
  • java面試常問的Runnable和Callable的區(qū)別

    java面試常問的Runnable和Callable的區(qū)別

    大家好,本篇文章主要講的是java面試常問的Runnable和Callable的區(qū)別,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下
    2022-01-01

最新評(píng)論