欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java不同線程解讀以及線程池的使用方式

 更新時(shí)間:2024年08月22日 10:58:12   作者:200.OK  
這篇文章主要介紹了java不同線程解讀以及線程池的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

java不同線程解讀以及線程池的使用

線程池子類比一個(gè)水池,而每一個(gè)線程,都好比水池的水。因此,水池多大,看系統(tǒng)硬件配置。線程池主要為了并發(fā),高效處理任務(wù)。而異步處理任務(wù),可以有效提高處理任務(wù)的吞吐量。

線程池的常見應(yīng)用場(chǎng)景

處理大量而短小的請(qǐng)求,請(qǐng)求數(shù)量很大,每一個(gè)請(qǐng)求開啟一個(gè)線程,在請(qǐng)求完畢之后再對(duì)線程進(jìn)行銷毀,這樣創(chuàng)建和銷毀線程所消耗的時(shí)間往往比任務(wù)本身所需消耗的資源要大得多。

線程池做到線程復(fù)用,不需要頻繁的創(chuàng)建和銷毀線程,線程池中的線程一直存在于線程池中,線程從任務(wù)隊(duì)列中取得任務(wù)來執(zhí)行。而且這樣做的另一個(gè)好處有,通過適當(dāng)?shù)卣{(diào)整線程池中的線程數(shù)目,也就是當(dāng)請(qǐng)求的數(shù)目超過某個(gè)閾值時(shí),就強(qiáng)制其它任何新到的請(qǐng)求一直等待,直到獲得一個(gè)線程來處理為止,從而可以防止資源不足。

線程池是什么?

線程池用于多線程處理中,它可以根據(jù)系統(tǒng)的情況,可以控制線程執(zhí)行的數(shù)量,優(yōu)化運(yùn)行效果。線程池做的工作主要是控制運(yùn)行的線程的數(shù)量,處理過程中將任務(wù)放入隊(duì)列,然后在線程創(chuàng)建后啟動(dòng)這些任務(wù),如果線程數(shù)量超過了最大數(shù)量超出數(shù)量的線程排隊(duì)等候,等其它線程執(zhí)行完畢,再?gòu)年?duì)列中取出任務(wù)來執(zhí)行。

線程池的作用

創(chuàng)建對(duì)象和銷毀對(duì)象是非常消耗時(shí)間和資源的。因此想要最小化這種消耗的一種思想就是『池化資源』,通過重用線程池中的資源來減少創(chuàng)建和銷毀線程所需要耗費(fèi)的時(shí)間和資源。

線程池的一個(gè)作用是創(chuàng)建和銷毀線程的次數(shù),每個(gè)工作線程可以多次使用;另一個(gè)作用是可根據(jù)系統(tǒng)情況調(diào)整執(zhí)行的線程數(shù)量,防止消耗過多內(nèi)存。另外,通過線程池,能有效的控制線程的最大并發(fā)數(shù),提高系統(tǒng)資源利用率,同時(shí)避免過多的資源競(jìng)爭(zhēng),避免堵塞。

線程池的優(yōu)點(diǎn)總結(jié)如下幾個(gè)方面:

  • 線程復(fù)用
  • 控制最大并發(fā)數(shù)
  • 管理線程

線程池組成

  • 線程池管理器:用于創(chuàng)建并管理線程池
  • 工作線程:線程池中的線程
  • 任務(wù)接口:每個(gè)任務(wù)必須實(shí)現(xiàn)的接口,用于工作線程調(diào)度其運(yùn)行
  • 任務(wù)隊(duì)列:用于存放待處理的任務(wù),提供一種緩沖機(jī)制

1、Async 和 @Async 很關(guān)鍵

兩個(gè)注解很關(guān)鍵:

  • @EnableAsync 啟用基于異步方法的執(zhí)行
  • @Async 這注解的函數(shù)會(huì)被異步處理

2、Thread和Runnable

Thread可以創(chuàng)建線程,但是線程使用完之后需要對(duì)線程資源進(jìn)行銷毀回收,消耗資源,且容易造成線程上下文切換(操作系統(tǒng)核心對(duì)CPU上對(duì)進(jìn)程或者線程進(jìn)行切換)問題,線程管理不當(dāng)容易資源耗盡,不建議使用。、

一個(gè)類實(shí)現(xiàn) Runnable 接口可以將該類的實(shí)例傳遞給一個(gè) Thread 對(duì)象并啟動(dòng)一個(gè)新線程,這個(gè)新線程就會(huì)執(zhí)行該類中 void run() 方法中所定義的邏輯。

3、數(shù)據(jù)類型—線程安全

如果有多線程共享數(shù)據(jù)的方式,要牢記各種使用場(chǎng)景的線程安全數(shù)據(jù)類型。

  • (1)、AtomicInteger 原子int整型;
  • (2)、AtomicLong 原子long整型;
  • (3)、AtomicBoolean 原子boolean;
  • (4)、List 這三種都是線程安全型:
①List<T> vector = new Vector<>();
②<T> listSyn = Collections.synchronizedList(new ArrayList<>()); 
③List<T> copyList = new CopyOnWriteArrayList<>();

4、@Configuration配置類和 @Bean將實(shí)例對(duì)象交給IOC容器

5、ThreadPoolExecutor 和 ThreadPoolTaskExecutor

兩種線程池方式,本質(zhì)上一樣,ThreadPoolTaskExecutor(我使用的) 源碼上是在 ThreadPoolExecutor 上再加了一層包裝,為了更方便在spring框架中使用。

  • 配置類: 可以確保異步執(zhí)行配置對(duì)應(yīng)用中的所有 bean 都生效。
  • 啟動(dòng)類: 啟動(dòng)類上整個(gè)應(yīng)用中啟用異步
  • 推薦: 針對(duì)應(yīng)用中的不同部分提供不同的異步執(zhí)行策略,或者只需要特定的一部分 bean 具備異步執(zhí)行能力,放配置類上。如果整個(gè)應(yīng)用都需要異步支持,放置在啟動(dòng)類。

使用@Bean(“beanName”)定義線程池 然后在@Async(“beanName”)中引用指定的線程池

@EnableAsync 用于啟用整個(gè)應(yīng)用程序的異步處理功能,包括所有通過 @Async 注解標(biāo)記的方法。它不負(fù)責(zé)配置底層線程池。

ThreadPoolTaskExecutor Bean 配置則是用于配置具體的線程池實(shí)例,這個(gè)線程池會(huì)被 @Async 方法所使用。

如果你在配置類中同時(shí)使用了 @EnableAsync 注解和自定義的 ThreadPoolTaskExecutor Bean 配置,Spring 將會(huì)使用你配置的線程池執(zhí)行異步方法。如果你只使用 @EnableAsync,Spring 將會(huì)使用默認(rèn)的線程池配置。

注意

  • 1、除了要在方法上加@Async注解,還需要在啟動(dòng)類加注解@EnableAsync啟動(dòng)多線程注解,@Async就會(huì)對(duì)標(biāo)注的方法開啟異步多線程調(diào)用,注意,這個(gè)方法的類一定要交給Spring容器來管理。
  • 2、法一定要從另一個(gè)類中調(diào)用,也就是從類的外部調(diào)用,類的內(nèi)部調(diào)用是無效的,因?yàn)锧Transactional和@Async注解的實(shí)現(xiàn)都是基于Spring的AOP,而AOP的實(shí)現(xiàn)是基于動(dòng)態(tài)代理模式實(shí)現(xiàn)的。那么注解失效的原因就很明顯了,有可能因?yàn)檎{(diào)用方法的是對(duì)象本身而不是代理對(duì)象,因?yàn)闆]有經(jīng)過Spring容器
  • 3、異步方法使用注解@Async的返回值只能為void或者Future
  • 4、方法必須是public方法

定義線程池的配置

package com.test.wll.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

@EnableAsync//或者加在app類上,此處不加需要在啟動(dòng)類上加
@Configuration 
public class ThreadPoolTaskConfig {

    @Bean("threadPoolRedisTaskExecutor")
    public ThreadPoolTaskExecutor threadPoolRedisTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //線程池創(chuàng)建的核心線程數(shù),線程池維護(hù)線程的最少數(shù)量,即使沒有任務(wù)需要執(zhí)行,也會(huì)一直存活
        executor.setCorePoolSize(8);
        //如果設(shè)置allowCoreThreadTimeout=true(默認(rèn)false)時(shí),核心線程會(huì)超時(shí)關(guān)閉
        //executor.setAllowCoreThreadTimeOut(true);
        //阻塞隊(duì)列 當(dāng)核心線程數(shù)達(dá)到最大時(shí),新任務(wù)會(huì)放在隊(duì)列中排隊(duì)等待執(zhí)行
        executor.setQueueCapacity(124);
        //最大線程池?cái)?shù)量,當(dāng)線程數(shù)>=corePoolSize,且任務(wù)隊(duì)列已滿時(shí)。線程池會(huì)創(chuàng)建新線程來處理任
        //任務(wù)隊(duì)列已滿時(shí), 且當(dāng)線程數(shù)=maxPoolSize,,線程池會(huì)拒絕處理任務(wù)而拋出異常
        executor.setMaxPoolSize(64);
        //當(dāng)線程空閑時(shí)間達(dá)到keepAliveTime時(shí),線程會(huì)退出,直到線程數(shù)量=corePoolSize
        //允許線程空閑時(shí)間30秒,當(dāng)maxPoolSize的線程在空閑時(shí)間到達(dá)的時(shí)候銷毀
        //如果allowCoreThreadTimeout=true,則會(huì)直到線程數(shù)量=0
        executor.setKeepAliveSeconds(30);
        //spring 提供的 ThreadPoolTaskExecutor 線程池,是有setThreadNamePrefix() 方法的。
        //jdk 提供的ThreadPoolExecutor 線程池是沒有 setThreadNamePrefix() 方法的
        executor.setThreadNamePrefix("threadPoolRedisTaskExecutor");
        // rejection-policy:拒絕策略:當(dāng)線程數(shù)已經(jīng)達(dá)到maxSize的時(shí)候,如何處理新任務(wù)
        // CallerRunsPolicy():交由調(diào)用方線程運(yùn)行,比如 main 線程;如果添加到線程池失敗,那么主線程會(huì)自己去執(zhí)行該任務(wù),不會(huì)等待線程池中的線程去執(zhí)行
        // AbortPolicy():該策略是線程池的默認(rèn)策略,如果線程池隊(duì)列滿了丟掉這個(gè)任務(wù)并且拋出RejectedExecutionException異常。
        // DiscardPolicy():如果線程池隊(duì)列滿了,會(huì)直接丟掉這個(gè)任務(wù)并且不會(huì)有任何異常
        // DiscardOldestPolicy():丟棄隊(duì)列中最老的任務(wù),隊(duì)列滿了,會(huì)將最早進(jìn)入隊(duì)列的任務(wù)刪掉騰出空間,再嘗試加入隊(duì)列
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
        executor.initialize();
        return executor;
    }
}
}
        //最大線程數(shù)
        executor.setMaxPoolSize(maxPoolSize);
        //核心線程數(shù)
        executor.setCorePoolSize(corePoolSize);
        //任務(wù)隊(duì)列的大小
        executor.setQueueCapacity(queueCapacity);
        //線程前綴名
        executor.setThreadNamePrefix(namePrefix);
        //線程存活時(shí)間
        executor.setKeepAliveSeconds(keepAliveSeconds);
    //此處的名字要與線程池的名字一致,清晰的命名可以清晰的了解哪個(gè)線程報(bào)錯(cuò)
    @Async("threadName")
    @GetMapping("/方法名字")
    public Result<String> test() {
}

6、拒絕策略

rejectedExectutionHandler參數(shù)字段用于配置絕策略,常用拒絕策略如下

  • AbortPolicy:用于被拒絕任務(wù)的處理程序,它將拋出RejectedExecutionException
  • CallerRunsPolicy:用于被拒絕任務(wù)的處理程序,直接在execute方法的調(diào)用線程中運(yùn)行被拒絕的任務(wù)。
  • DiscardOldestPolicy:用于被拒絕任務(wù)的處理程序,放棄最舊的未處理請(qǐng)求,然后重試execute。
  • DiscardPolicy:用于被拒絕任務(wù)的處理程序,默認(rèn)情況下它將丟棄被拒絕的任務(wù)。

7、線程池處理流程

線程池的工作流程(必知必會(huì))?

這個(gè)問題回答的時(shí)候,最好用講故事的方式進(jìn)行。 假如核心線程數(shù)是5,最大線程數(shù)是10,阻塞隊(duì)列也是10

  • 1)有新任務(wù)來的時(shí)候,將先使用核心線程執(zhí)行;
  • 2)當(dāng)任務(wù)數(shù)達(dá)到5個(gè)的時(shí)候,第6個(gè)任務(wù)開始排隊(duì);
  • 3)當(dāng)任務(wù)數(shù)達(dá)到15個(gè)的時(shí)候,第16個(gè)任務(wù)將開啟新的線程執(zhí)行,也就是第6個(gè)線程
  • 4)當(dāng)任務(wù)數(shù)達(dá)到20個(gè)的時(shí)候,線程池滿了,如果有第21個(gè)任務(wù),將執(zhí)行拒絕策略

8、 Hutool 的ThreadUtil.execute

execute 方法是通過 GlobalThreadPool 來執(zhí)行任務(wù)的,而 GlobalThreadPool 在 Hutool 中是一個(gè)全局的線程池。

它會(huì)使用默認(rèn)的配置來初始化線程池,這些默認(rèn)配置在 Hutool 內(nèi)部已經(jīng)設(shè)定好了,因此在使用 execute 方法時(shí)不需要手動(dòng)指定核心池大小和最大池大小。

這種方法適合簡(jiǎn)單的任務(wù)執(zhí)行,如果需要更靈活的線程池配置(比如自定義線程數(shù)、隊(duì)列類型等),可以考慮使用 newExecutor 等方法手動(dòng)創(chuàng)建線程池,并進(jìn)行更詳細(xì)的配置。

spring的 ThreadPoolTaskExecutor

  • 優(yōu)點(diǎn): Spring 的 ThreadPoolTaskExecutor 是 Spring 框架提供的一個(gè)線程池實(shí)現(xiàn),它提供了很多配置選項(xiàng),允許你更加靈活地定制線程池的行為,比如核心線程數(shù)、最大線程數(shù)、隊(duì)列容量、線程存活時(shí)間等。
  • 適用情況: 適合在 Spring 環(huán)境中使用,對(duì)于基于 Spring 的項(xiàng)目,使用這種方式可以充分利用 Spring 提供的特性和管理能力,例如可以方便地集成到 Spring 的任務(wù)調(diào)度中

Hutool ThreadUtil

  • 優(yōu)點(diǎn): Hutool 提供的 ThreadUtil 類是一個(gè)簡(jiǎn)化了的工具類,提供了一些靜態(tài)方法來方便地創(chuàng)建線程池和執(zhí)行任務(wù)。它是一個(gè)輕量級(jí)的工具庫(kù),適合在一般的 Java 程序中使用,不依賴于 Spring 框架。
  • 適用情況: 適合非 Spring 項(xiàng)目或者不需要集成到 Spring 容器管理的場(chǎng)景。如果你不需要太多的線程池配置選項(xiàng),而只是簡(jiǎn)單地執(zhí)行任務(wù),使用 ThreadUtilexecute 方法會(huì)更加便捷。

常見線程池

  • 1)定長(zhǎng)線程池(FixedThreadPool)
  • 2)定時(shí)線程池(ScheduledThreadPool)
  • 3)可緩存線程池(CachedThreadPool)
  • 4)單線程化線程池(SingleThreadExecutor)

核心概念:這四個(gè)線程池的本質(zhì)都是ThreadPoolExecutor對(duì)象(看源碼)

不同點(diǎn)在于:

  • 1)FixedThreadPool:只有核心線程,線程數(shù)量固定,執(zhí)行完立即回收,任務(wù)隊(duì)列為鏈表結(jié)構(gòu)的有界隊(duì)列。
  • 2)ScheduledThreadPool:核心線程數(shù)量固定,非核心線程數(shù)量無限,執(zhí)行完閑置 10ms 后回收,任務(wù)隊(duì)列為延時(shí)阻塞隊(duì)列。
  • 3)CachedThreadPool:無核心線程,非核心線程數(shù)量無限,執(zhí)行完閑置 60s 后回收,任務(wù)隊(duì)列為不存儲(chǔ)元素的阻塞隊(duì)列。
  • 4)SingleThreadExecutor:只有 1 個(gè)核心線程,無非核心線程,執(zhí)行完立即回收,任務(wù)隊(duì)列為鏈表結(jié)構(gòu)的有界隊(duì)列

線程池的主要參數(shù)有哪些(必知必會(huì))?

  • 1)corePoolSize(必需):核心線程數(shù)。默認(rèn)情況下,核心線程會(huì)一直存活,但是當(dāng)將 allowCoreThreadTimeout 設(shè)置為 true 時(shí),核心線程也會(huì)超時(shí)回收。
  • 2)maximumPoolSize(必需):線程池所能容納的最大線程數(shù)。當(dāng)活躍線程數(shù)達(dá)到該數(shù)值后,后續(xù)的新任務(wù)將會(huì)阻塞。
  • 3)keepAliveTime(必需):線程閑置超時(shí)時(shí)長(zhǎng)。如果超過該時(shí)長(zhǎng),非核心線程就會(huì)被回收。如果將 allowCoreThreadTimeout 設(shè)置為 true 時(shí),核心線程也會(huì)超時(shí)回收。
  • 4)unit(必需):指定 keepAliveTime 參數(shù)的時(shí)間單位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。
  • 5)workQueue(必需):任務(wù)隊(duì)列。通過線程池的 execute() 方法提交的 Runnable 對(duì)象將存儲(chǔ)在該參數(shù)中。其采用阻塞隊(duì)列實(shí)現(xiàn)。
  • 6)threadFactory(可選):線程工廠。用于指定為線程池創(chuàng)建新線程的方式。
  • 7)handler(可選):拒絕策略。當(dāng)達(dá)到最大線程數(shù)時(shí)需要執(zhí)行的飽和策略。

線程池的工作流程(必知必會(huì))?

這個(gè)問題回答的時(shí)候,最好用講故事的方式進(jìn)行。 假如核心線程數(shù)是5,最大線程數(shù)是10,阻塞隊(duì)列也是10

  • 1)有新任務(wù)來的時(shí)候,將先使用核心線程執(zhí)行;
  • 2)當(dāng)任務(wù)數(shù)達(dá)到5個(gè)的時(shí)候,第6個(gè)任務(wù)開始排隊(duì);
  • 3)當(dāng)任務(wù)數(shù)達(dá)到15個(gè)的時(shí)候,第16個(gè)任務(wù)將開啟新的線程執(zhí)行,也就是第6個(gè)線程
  • 4)當(dāng)任務(wù)數(shù)達(dá)到20個(gè)的時(shí)候,線程池滿了,如果有第21個(gè)任務(wù),將執(zhí)行拒絕策略

線程安全

函數(shù)、函數(shù)庫(kù)在并發(fā)環(huán)境中被調(diào)用時(shí),能夠正確地處理多個(gè)線程之間的共享變量,使程序功能正確完成。

  • 并發(fā):(由cpu分配時(shí)間片,看似像同時(shí)干多個(gè)事情實(shí)際是由不同的cpu時(shí)間片干)任務(wù)執(zhí)行時(shí)間比調(diào)度頻率長(zhǎng),會(huì)導(dǎo)致任務(wù)堆積,任務(wù)完成后,才能開始下一個(gè)任務(wù),可能會(huì)造成任務(wù)被阻塞,直到有空閑線程可用。當(dāng)任務(wù)執(zhí)行時(shí)間比調(diào)度頻率長(zhǎng)時(shí),會(huì)出現(xiàn)并發(fā)問題,線程池中的線程會(huì)被任務(wù)堵塞,直到前一個(gè)任務(wù)執(zhí)行完成。
  • 并行:當(dāng)系統(tǒng)有一個(gè)以上CPU時(shí),當(dāng)一個(gè)CPU執(zhí)行一個(gè)進(jìn)程時(shí),另一個(gè)CPU可以執(zhí)行另一個(gè)進(jìn)程,兩個(gè)進(jìn)程互不搶占CPU資源,可以同時(shí)進(jìn)行,這種方式我們稱之為并行(Parallel)。

eg:并發(fā)是兩個(gè)隊(duì)伍交替使用一臺(tái)咖啡機(jī)。并行是兩個(gè)隊(duì)伍同時(shí)使用兩臺(tái)咖啡機(jī)。

  • 線程和進(jìn)程:下載為進(jìn)程,下載的多個(gè)視頻為線程,共享線程資源
  • 共享變量:指的是多個(gè)線程都可以操作的變量

保存在方法區(qū)中的變量就是Java中的共享變量

堆中存放new出來的對(duì)象,(包括實(shí)例變量); 棧中存放正在調(diào)用的方法中的局部變量 (包括方法的參數(shù)); 方法區(qū)中存儲(chǔ).class 字節(jié)碼 文件(包括 靜態(tài)變量 、靜態(tài)方法)

public class Variables {
    // 類變量  共享變量
    private static int a;
    //成員變量  共享變量
    private int b;
    //局部變量  c和d 非共享變量
    public void test(int c){
        int d;
    }
}
//多線程場(chǎng)景,對(duì)于變量a和b的操作是需要考慮線程安全的,而對(duì)于線程c和d的操作是不需要考慮線程安全的。

線程不安全

多線程并發(fā)執(zhí)行某個(gè)代碼時(shí),產(chǎn)生了邏輯上的錯(cuò)誤,結(jié)果和預(yù)期值不相同 線程安全是指多線程執(zhí)行時(shí)沒有產(chǎn)生邏輯錯(cuò)誤,結(jié)果和預(yù)期值相同

//開啟了兩個(gè)線程,每個(gè)線程執(zhí)行1000次循環(huán),循環(huán)中對(duì)count進(jìn)行加1操作。等待兩個(gè)線程都執(zhí)行完成后,打印count的值。
public class Test {
     private  static  int count;
    private static class Thread1 extends Thread {
        public void run() {
            for (int i = 0; i < 1000; i++) {
                count ++;
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Thread1  t1 = new Thread1();
        Thread1  t2 = new Thread1();
        t1.start();
        t2.start();
        //main主線程內(nèi)調(diào)用join()方法:休眠主線程,等待t1、t2線程執(zhí)行完畢主線程再繼續(xù),即最后輸出count值
        t1.join();
        t2.join();
        System.out.println(count);
    }
}

輸出結(jié)果應(yīng)該是2000但是不有時(shí)候1996

count++的指令在實(shí)際執(zhí)行的過程中不是原子性的,而是要分為讀、改、寫三步來進(jìn)行;即先從內(nèi)存中讀出count的值,然后執(zhí)行+1操作,再將結(jié)果寫回內(nèi)存中,如下圖所示。

上圖中線程1執(zhí)行了兩次自加操作,而線程2執(zhí)行了一次自加操作,但是count卻從6變成了8,只加了2。

我們看一下為什么會(huì)出現(xiàn)這種情況。當(dāng)線程1讀取count的值為6完成后,此時(shí)切換到了線程2執(zhí)行,線程2同樣讀取到了count的值為6,而后進(jìn)行改和寫操作,count的值變?yōu)榱?;此時(shí)線程又切回了線程1,但是線程1中count的值依然是線程2修改前的6,這就是問題所在!即線程2修改了count的值,但是這種修改對(duì)線程1不可見,導(dǎo)致了程序出現(xiàn)了線程不安全的問題,沒有符合我們預(yù)期的邏輯。

導(dǎo)致線程不安全的原因

主要有三點(diǎn):

  • 不滿足原子性:一個(gè)或者多個(gè)操作在 CPU 執(zhí)行的過程中被中斷,當(dāng) cpu 執(zhí)行一個(gè)線程過程時(shí),調(diào)度器可能調(diào)走CPU,去執(zhí)行另一個(gè)線程,此線程的操作可能還沒有結(jié)束;(synchronized鎖解決)
  • 不滿足可見性:一個(gè)線程對(duì)共享變量的修改,另外一個(gè)線程不能立刻看到
  • 不滿足有序性:程序執(zhí)行的順序沒有按照代碼的先后順序執(zhí)行三、怎樣解決線程不安全

Java中的原子操作包括:

  • 除long和double之外的基本類型的賦值操作
  • 所有引用reference的賦值操作
  • java.concurrent.Atomic.* 包中所有類的一切操作

可見性是指當(dāng)多個(gè)線程訪問同一個(gè)變量時(shí),一個(gè)線程修改了這個(gè)變量的值,其他線程能夠立即看得到修改的值。

volatile關(guān)鍵字來保證可見性。當(dāng)一個(gè)共享變量被volatile修飾時(shí),它會(huì)保證修改的值會(huì)立即被更新到主存,當(dāng)有其他線程需要讀取時(shí),它會(huì)去內(nèi)存中讀取新值。而普通的共享變量不能保證可見性,因?yàn)槠胀ü蚕碜兞勘恍薷闹?,什么時(shí)候被寫入主存是不確定的,當(dāng)其他線程去讀取時(shí),此時(shí)內(nèi)存中可能還是原來的舊值,因此無法保證可見性。另外,通過synchronized和Lock也能夠保證可見性,synchronized和Lock能保證同一時(shí)刻只有一個(gè)線程獲取鎖然后執(zhí)行同步代碼,并且在釋放鎖之前會(huì)將對(duì)變量的修改刷新到主存當(dāng)中。因此可以保證可見性。

volatile關(guān)鍵字來保證一定的“有序性”。另外可以通過synchronized和Lock來保證有序性,很顯然,synchronized和Lock保證每個(gè)時(shí)刻是有一個(gè)線程執(zhí)行同步代碼,相當(dāng)于是讓線程順序執(zhí)行同步代碼,自然就保證了有序性。另外,Java內(nèi)存模型具備一些先天的“有序性”,即不需要通過任何手段就能夠得到保證的有序性,這個(gè)通常也稱為 happens-before 原則。如果兩個(gè)操作的執(zhí)行次序無法從happens-before原則推導(dǎo)出來,那么它們就不能保證它們的有序性,虛擬機(jī)可以隨意地對(duì)它們進(jìn)行重排序。

補(bǔ)充:happens-before原則(先行發(fā)生原則)

  • 程序次序規(guī)則:一個(gè)線程內(nèi),按照代碼順序,書寫在前面的操作先行發(fā)生于書寫在后面的操作。
  • 鎖定規(guī)則:一個(gè)unLock操作先行發(fā)生于后面對(duì)同一個(gè)鎖額lock操作。
  • volatile變量規(guī)則:對(duì)一個(gè)變量的寫操作先行發(fā)生于后面對(duì)這個(gè)變量的讀操作。
  • 傳遞規(guī)則:如果操作A先行發(fā)生于操作B,而操作B又先行發(fā)生于操作C,則可以得出操作A先行發(fā)生于操作C。
  • 線程啟動(dòng)規(guī)則:Thread對(duì)象的start()方法先行發(fā)生于此線程的每個(gè)一個(gè)動(dòng)作。
  • 線程中斷規(guī)則:對(duì)線程interrupt()方法的調(diào)用先行發(fā)生于被中斷線程的代碼檢測(cè)到中斷事件的發(fā)生。
  • 線程終結(jié)規(guī)則:線程中所有的操作都先行發(fā)生于線程的終止檢測(cè),我們可以通過Thread.join()方法結(jié)束、Thread.isAlive()的返回值手段檢測(cè)到線程已經(jīng)終止執(zhí)行。
  • 對(duì)象終結(jié)規(guī)則:一個(gè)對(duì)象的初始化完成先行發(fā)生于他的finalize()方法的開始。

總結(jié)

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

相關(guān)文章

  • SpringBoot配置項(xiàng)目訪問路徑URL的根路徑方式

    SpringBoot配置項(xiàng)目訪問路徑URL的根路徑方式

    這篇文章主要介紹了SpringBoot配置項(xiàng)目訪問路徑URL的根路徑方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • MybatisPlus中的insert操作詳解

    MybatisPlus中的insert操作詳解

    這篇文章主要介紹了MybatisPlus中的insert操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • Java中集合LinkedList的原理與使用方法

    Java中集合LinkedList的原理與使用方法

    這篇文章主要給大家介紹了關(guān)于Java中集合LinkedList的原理與使用方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • 解決maven打包排除類不生效maven-compiler-plugin問題

    解決maven打包排除類不生效maven-compiler-plugin問題

    總結(jié):在Spring Boot項(xiàng)目B中作為項(xiàng)目A的依賴時(shí),排除啟動(dòng)類不生效的原因是被其他類引用或父POM引入,解決方法是跳過test編譯或注釋掉@SpringBootTest(classes={BApplication.class})
    2024-11-11
  • 區(qū)分java中String+String和String+char

    區(qū)分java中String+String和String+char

    這篇文章主要向大家詳細(xì)區(qū)分了java中String+String和String+char,感興趣的小伙伴們可以參考一下
    2016-01-01
  • java9新特性Collection集合類的增強(qiáng)與優(yōu)化方法示例

    java9新特性Collection集合類的增強(qiáng)與優(yōu)化方法示例

    這篇文章主要為大家介紹了java9新特性Collection集合類的增強(qiáng)與優(yōu)化方法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2022-03-03
  • JavaSE異常Exception處理方法以及自定義

    JavaSE異常Exception處理方法以及自定義

    網(wǎng)絡(luò)異常exception是指在網(wǎng)絡(luò)通信過程中出現(xiàn)的異常情況,這些異??赡苁怯捎诰W(wǎng)絡(luò)連接不穩(wěn)定、網(wǎng)絡(luò)故障、服務(wù)器故障、網(wǎng)絡(luò)擁堵等原因?qū)е碌?這篇文章主要給大家介紹了關(guān)于JavaSE異常Exception處理方法以及自定義的相關(guān)資料,需要的朋友可以參考下
    2024-07-07
  • SpringBoot+Docker+IDEA實(shí)現(xiàn)一鍵構(gòu)建+推送、運(yùn)行、同鏡像多容器啟動(dòng)

    SpringBoot+Docker+IDEA實(shí)現(xiàn)一鍵構(gòu)建+推送、運(yùn)行、同鏡像多容器啟動(dòng)

    這篇文章主要介紹了SpringBoot+Docker+IDEA實(shí)現(xiàn)一鍵構(gòu)建+推送、運(yùn)行、同鏡像多容器啟動(dòng),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • Java字符串常量池示例詳解

    Java字符串常量池示例詳解

    作為最基礎(chǔ)的引用數(shù)據(jù)類型,Java設(shè)計(jì)者為 String 提供了字符串常量池以提高其性能,下面這篇文章主要給大家介紹了關(guān)于Java字符串常量池的相關(guān)資料,需要的朋友可以參考下
    2021-08-08
  • activiti實(shí)現(xiàn)員工請(qǐng)假流程解析

    activiti實(shí)現(xiàn)員工請(qǐng)假流程解析

    這篇文章主要介紹了activiti實(shí)現(xiàn)員工請(qǐng)假流程解析,本文通過實(shí)例代碼圖文相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07

最新評(píng)論