最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java線程同步及實現(xiàn)方法詳解

 更新時間:2023年11月09日 09:39:12   作者:y_initiate  
這篇文章主要介紹了Java線程同步及實現(xiàn)方法詳解,當我們有多個線程要同時訪問一個變量或?qū)ο髸r,如果這些線程中既有讀又有寫操作時,就會導致變量值或?qū)ο蟮臓顟B(tài)出現(xiàn)混亂,從而導致程序異常,需要的朋友可以參考下

1. 什么是線程同步?

首先,引用一個非常經(jīng)典的例子來說明為什么要進行線程同步

當我們有多個線程要同時訪問一個變量或?qū)ο髸r,如果這些線程中既有讀又有寫操作時,就會導致變量值或?qū)ο蟮臓顟B(tài)出現(xiàn)混亂,從而導致程序異常。 舉個例子,動物園有三個窗口同時在售賣門票,假設(shè)還剩最后一張門票時,有兩個窗口同時有人在買門票,此時兩個窗口都觀察到還有一張門票,于是兩個窗口都選擇了賣出,此時門票數(shù)變成了-1,出現(xiàn)錯誤。

還可能會出現(xiàn)其他情況的錯誤,比如剩余10張票時,兩個窗口同時售賣出一張票后修改票數(shù)為9。

package test;
import java.io.*;
public class TicketThreadTest {
    public static void main(String[] args) throws IOException, InterruptedException {
        TicketThread ticket1 = new TicketThread();
        Thread thread1 = new Thread(ticket1, "窗口1");
        Thread thread2 = new Thread(ticket1, "窗口2");
        Thread thread3 = new Thread(ticket1, "窗口3");
        thread1.start();
        thread2.start();
        thread3.start();
        thread1.join(); // 等待三個線程結(jié)束后打印賣出總數(shù)
        thread2.join();
        thread3.join();
        System.out.println("sellNum: " + TicketThread.sellNum);
    }
}
class TicketThread implements Runnable {
    private static int ticketNum = 20; // 總票數(shù)
    public static int sellNum = 0; // 統(tǒng)計賣出總票數(shù)
    @Override
    public void run() {
        while (true) {
            if (ticketNum > 0) {
                System.out.println(Thread.currentThread().getName() + "賣出了一張票,剩余:" + --ticketNum);  // 賣出一張票
                sellNum++;  // 賣出總票數(shù)加1
            } else {
                break;
            }
            try {
                Thread.sleep(10);  // 每次sleep 10ms,提高出錯可能
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

這是某次程序運行結(jié)果,很顯然賣出29張票,發(fā)生了錯誤

窗口3賣出了一張票,剩余:19
窗口1賣出了一張票,剩余:18
窗口2賣出了一張票,剩余:17
窗口3賣出了一張票,剩余:16
窗口1賣出了一張票,剩余:16
窗口2賣出了一張票,剩余:16
窗口1賣出了一張票,剩余:15
窗口3賣出了一張票,剩余:14
窗口2賣出了一張票,剩余:15
窗口2賣出了一張票,剩余:13
窗口3賣出了一張票,剩余:12
窗口1賣出了一張票,剩余:13
窗口3賣出了一張票,剩余:9
窗口1賣出了一張票,剩余:11
窗口2賣出了一張票,剩余:10
窗口1賣出了一張票,剩余:7
窗口3賣出了一張票,剩余:8
窗口2賣出了一張票,剩余:8
窗口1賣出了一張票,剩余:5
窗口3賣出了一張票,剩余:6
窗口2賣出了一張票,剩余:6
窗口2賣出了一張票,剩余:4
窗口3賣出了一張票,剩余:3
窗口1賣出了一張票,剩余:4
窗口1賣出了一張票,剩余:2
窗口3賣出了一張票,剩余:2
窗口2賣出了一張票,剩余:1
窗口2賣出了一張票,剩余:-1
窗口1賣出了一張票,剩余:0
sellNum: 29

2. Java線程同步方法

Java線程同步有7種方法

  • 使用 synchronized關(guān)鍵字實現(xiàn)線程同步
  • 使用wait和notify實現(xiàn)線程同步
  • 使用特殊域變量(volatile)實現(xiàn)線程同步
  • 使用重入鎖實現(xiàn)線程同步,在JavaSE5.0中新增了一個java.util.concurrent包來支持同步
  • 使用局部變量實現(xiàn)線程同步,如果使用ThreadLocal管理變量,則每一個使用該變量的線程都獲得該變量的副本,副本之間相互獨立,這樣每一個線程都可以隨意修改自己的變量副本,而不會對其他線程產(chǎn)生影響。
  • 使用阻塞隊列實現(xiàn)線程同步
  • 使用原子變量實現(xiàn)線程同步

3 使用synchronized實現(xiàn)線程同步

synchronized的作用主要有三個:

  • 原子性:確保線程互斥地訪問同步代碼
  • 可見性:保證共享變量的修改能夠及時可見

可見性是通過Java內(nèi)存模型中的“對一個變量unlock操作之前,必須要同步到主內(nèi)存中;如果對一個變量進行l(wèi)ock操作,則將會清空工作內(nèi)存中此變量的值,在執(zhí)行引擎使用此變量前,需要重新從主內(nèi)存中l(wèi)oad操作或assign操作初始化變量值”來保證的

  • 有序性:有效解決重排序問題,即 “一個unlock操作先行發(fā)生(happen-before)于后面對同一個鎖的lock操作”

happen-before:If one action _happens-before _another, then the first is visible to and ordered before the second. 如果指令甲happens-before指令乙,那么指令甲必須排序在指令乙之前,并且指令甲的執(zhí)行結(jié)果對指令乙可見。

3.1 同步代碼塊

同步代碼塊是通過鎖定一個指定的對象,來對同步代碼塊中的代碼進行同步。 一個線程訪問一個對象中的synchronized(this)同步代碼塊時,其他試圖訪問該代碼塊的線程將被阻塞。 注意synchronized必須鎖住的是指定的對象,不同對象間不會阻塞,如果需要鎖住類對象,只需要使用synchronized(Class clazz)鎖住類即可。

我們使用同步代碼塊來解決售票問題

package test;
import java.io.*;
public class TicketThreadTest {
    public static void main(String[] args) throws IOException, InterruptedException {
        TicketThread ticket1 = new TicketThread();
        Thread thread1 = new Thread(ticket1, "窗口1");
        Thread thread2 = new Thread(ticket1, "窗口2");
        Thread thread3 = new Thread(ticket1, "窗口3");
        thread1.start();
        thread2.start();
        thread3.start();
        thread1.join(); // 等待三個線程結(jié)束后打印賣出總數(shù)
        thread2.join();
        thread3.join();
        System.out.println("sellNum: " + TicketThread.sellNum);
    }
}
class TicketThread implements Runnable {
    private static int ticketNum = 20; // 總票數(shù)
    public static int sellNum = 0; // 統(tǒng)計賣出總票數(shù)
    @Override
    public void run() {
        while (true) {
            synchronized (this) {	// 鎖住對共享變量的訪問
                if (ticketNum > 0) {
                    System.out.println(Thread.currentThread().getName() + "賣出了一張票,剩余:" + --ticketNum);  // 賣出一張票
                    sellNum++;  // 賣出總票數(shù)加1
                } else {
                    break;
                }
            }
            try {
                Thread.sleep(10);  // 每次sleep 10ms,提高出錯可能
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

從運行結(jié)果可以看到synchronized修飾的代碼塊同一時間只能有一個線程訪問

窗口1賣出了一張票,剩余:19
窗口2賣出了一張票,剩余:18
窗口3賣出了一張票,剩余:17
窗口3賣出了一張票,剩余:16
窗口2賣出了一張票,剩余:15
窗口1賣出了一張票,剩余:14
窗口1賣出了一張票,剩余:13
窗口2賣出了一張票,剩余:12
窗口3賣出了一張票,剩余:11
窗口2賣出了一張票,剩余:10
窗口3賣出了一張票,剩余:9
窗口1賣出了一張票,剩余:8
窗口1賣出了一張票,剩余:7
窗口2賣出了一張票,剩余:6
窗口3賣出了一張票,剩余:5
窗口2賣出了一張票,剩余:4
窗口3賣出了一張票,剩余:3
窗口1賣出了一張票,剩余:2
窗口3賣出了一張票,剩余:1
窗口1賣出了一張票,剩余:0
sellNum: 20

注意上述類中的ticketNum和sellNum都屬于類對象,如果我們使用不同的實例對象,使用synchronized(this)鎖住的不是同一個對象,會發(fā)現(xiàn)并沒有實現(xiàn)線程同步,此時就需要鎖住synchronized(this.getClass())。

使用不同實例對象,main方法中修改如下:

//使用不同實例對象,main方法中修改如下:
TicketThread ticket1 = new TicketThread();
TicketThread ticket2 = new TicketThread();
TicketThread ticket3 = new TicketThread();
Thread thread1 = new Thread(ticket1, “窗口1”);
Thread thread2 = new Thread(ticket2, “窗口2”);
Thread thread3 = new Thread(ticket3, “窗口3”);
//TicketThread類中修改為
synchronized(this.getClass())或synchronized(TicketThread.class)

3.2 同步方法

同步方法是對這個方法塊里的代碼進行同步,而這種情況下鎖定的對象就是方法所屬的對象自身。

相當于使用synchronized(this)鎖住方法中的代碼

如果這個方法是靜態(tài)同步方法呢?那么線程鎖定的就不是這個類的對象了,而是這個類對應的java.lang.Class類型的對象。

相當于使用synchronized(this.getClass())鎖住方法中的代碼

**注意:**當一個同步方法或者同步塊被某個線程執(zhí)行時,這個對象就被鎖定了,其他線程無法在此時訪問這個對象的同步方法,也不能執(zhí)行同步塊,但可以訪問非同步方法中的非同步代碼塊。

上述售票問題使用同步方法實現(xiàn)線程同步

package test;
import java.io.*;
public class TicketThreadTest {
    public static void main(String[] args) throws IOException, InterruptedException {
        TicketThread ticket1 = new TicketThread();
        Thread thread1 = new Thread(ticket1, "窗口1");
        Thread thread2 = new Thread(ticket1, "窗口2");
        Thread thread3 = new Thread(ticket1, "窗口3");
        thread1.start();
        thread2.start();
        thread3.start();
        thread1.join(); // 等待三個線程結(jié)束后打印賣出總數(shù)
        thread2.join();
        thread3.join();
        System.out.println("sellNum: " + TicketThread.sellNum);
    }
}
class TicketThread implements Runnable {
    private static int ticketNum = 20; // 總票數(shù)
    public static int sellNum = 0; // 統(tǒng)計賣出總票數(shù)
    @Override
    public void run() {
        while (true) {
            if(!sellOneTicket()){
                break;
            }
            try {
                Thread.sleep(10);  // 每次sleep 10ms,提高出錯可能
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    private synchronized boolean sellOneTicket(){
        if (ticketNum > 0) {
            System.out.println(Thread.currentThread().getName() + "賣出了一張票,剩余:" + --ticketNum);  // 賣出一張票
            sellNum++;  // 賣出總票數(shù)加1
            return true;
        } else {
            return false;
        }
    }
}

到此這篇關(guān)于Java線程同步及實現(xiàn)方法詳解的文章就介紹到這了,更多相關(guān)Java線程同步內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java?環(huán)境配置(2023年詳細教程)

    java?環(huán)境配置(2023年詳細教程)

    這篇文章首先為了完善我的知識體系,今后一些軟件的安裝教程也可能會用到想寫一個更加詳細的,因為這并不僅僅是寫給?IT?行業(yè)的,其它行業(yè)可能也需要配置java環(huán)境
    2023-06-06
  • SpringCloud微服務(wù)之Config知識總結(jié)

    SpringCloud微服務(wù)之Config知識總結(jié)

    今天帶大家學習SpringCloud微服務(wù)中的Config的相關(guān)知識,文中有非常詳細的介紹,對正在學習SpringCloud微服務(wù)的小伙伴們有很好地幫助,需要的朋友可以參考下
    2021-05-05
  • Java中的RestTemplate使用詳解

    Java中的RestTemplate使用詳解

    這篇文章主要介紹了Java中的RestTemplate使用詳解,Spring內(nèi)置了RestTemplate作為Http請求的工具類,簡化了很多操作,雖然Spring5推出了WebClient,但是整體感覺還是RestTemplate用起來更簡單方便一些,需要的朋友可以參考下
    2023-10-10
  • spring 和 spring boot 中的屬性配置方式

    spring 和 spring boot 中的屬性配置方式

    這篇文章主要介紹了spring 和 spring boot 中的屬性配置方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 詳解Java編程中protected修飾符與static修飾符的作用

    詳解Java編程中protected修飾符與static修飾符的作用

    這篇文章主要介紹了Java編程中protected關(guān)鍵字與static關(guān)鍵字的作用,是Java入門學習中的基礎(chǔ)知識,需要的朋友可以參考下
    2016-01-01
  • Java創(chuàng)建、讀取和更新Excel文檔的實現(xiàn)指南

    Java創(chuàng)建、讀取和更新Excel文檔的實現(xiàn)指南

    還在用Java代碼逐行雕刻Excel文檔嗎,是時候升級你的生產(chǎn)力工具箱了,借助Spire.XLS for Java,無論是創(chuàng)建新報表、讀取關(guān)鍵數(shù)據(jù)還是實時更新內(nèi)容,現(xiàn)在你都能以簡單的代碼指令輕松實現(xiàn),所以本文給大家介紹了如何使用Java創(chuàng)建、讀取和更新Excel文檔,需要的朋友可以參考下
    2025-10-10
  • Java8中的默認方法(面試者必看)

    Java8中的默認方法(面試者必看)

    這篇文章主要介紹了Java8中的默認方法(面試者必看),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-11-11
  • springboot捕獲全局異常實現(xiàn)過程

    springboot捕獲全局異常實現(xiàn)過程

    本文主要介紹了Java中的異常和錯誤,包括Exception和Error的區(qū)別、如何捕捉全局異常、自定義異常的實現(xiàn)等,通過實例代碼和步驟,展示了如何在Spring?Boot項目中實現(xiàn)全局異常處理,并自定義異常類來增強程序的健壯性
    2026-03-03
  • Java中final關(guān)鍵字的使用與注意總結(jié)

    Java中final關(guān)鍵字的使用與注意總結(jié)

    這篇文章主要給大家介紹了關(guān)于Java中final關(guān)鍵字的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者使用Java具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2020-08-08
  • WebSocket無法注入屬性的問題及解決方案

    WebSocket無法注入屬性的問題及解決方案

    這篇文章主要介紹了WebSocket無法注入屬性的問題及解決方法,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-09-09

最新評論

富宁县| 淄博市| 乌拉特前旗| 塔河县| 富川| 定陶县| 玛曲县| 西充县| 静海县| 镇赉县| 光山县| 商丘市| 罗定市| 洛隆县| 灵武市| 湖口县| 太和县| 东山县| 濉溪县| 招远市| 淮安市| 马山县| 明光市| 瑞金市| 邯郸市| 手游| 翁牛特旗| 榆社县| 柏乡县| 江口县| 郁南县| 克东县| 永寿县| 台前县| 保定市| 博乐市| 孝感市| 调兵山市| 青铜峡市| 瑞昌市| 永登县|