Java線程安全狀態(tài)專題解析
一、觀察線程的所有狀態(tài)
線程的狀態(tài)是一個(gè)枚舉類型 Thread.State
public static void main(String[] args) {
for (Thread.State state : Thread.State.values()){
System.out.println(state);
}
}
NEW: 安排了工作, 還未開始行動(dòng)
RUNNABLE: 可工作的. 又可以分成正在工作中和即將開始工作.就緒狀態(tài)
BLOCKED: 這幾個(gè)都表示排隊(duì)等著其他事情
WAITING: 這幾個(gè)都表示排隊(duì)等著其他事情
TIMED_WAITING: 這幾個(gè)都表示排隊(duì)等著其他事情
TERMINATED: 工作完成了.
二、線程狀態(tài)和狀態(tài)轉(zhuǎn)移的意義

NEW:Thread對(duì)象有了,但是PCB還沒有
RUNNABLE:線程正在CPU上執(zhí)行或者即將到CPU上執(zhí)行(PCB在就緒隊(duì)列中,隨時(shí)可能被調(diào)度到)
WAITING:wait方法導(dǎo)致
TIMED_WAITING:sleep方法導(dǎo)致
BLOCKED:等待鎖導(dǎo)致
TERMINATED:對(duì)象還在,但PCB已經(jīng)沒了
public static void main(String[] args) {
Thread t = new Thread(){
@Override
public void run() {
for (int i = 0; i < 100_00; i++){
}
}
};
System.out.println("線程啟動(dòng)前:" + t.getState());
t.start();
while (t.isAlive()){
System.out.println("線程運(yùn)行中:" + t.getState());
}
System.out.println("線程結(jié)束后:" + t.getState());
}

三、多線程帶來的風(fēng)險(xiǎn)
線程不安全的原因
①線程是搶占式執(zhí)行的
線程不安全的萬惡之源,線程之間的調(diào)度完全由內(nèi)核負(fù)責(zé),用戶代碼中感知不到,也無法控制。線程之間誰先執(zhí)行,誰后執(zhí)行,誰執(zhí)行到哪里從CPU上下來,這樣的過程用戶無法控制也無法感知到的。
②自增操作不是原子的
每次++都能拆成三個(gè)步驟
? ? ? ? 把內(nèi)存中的數(shù)據(jù)讀取到CPU中
? ? ? ? 把CPU中的數(shù)據(jù)+1
? ? ? ? 把計(jì)算的數(shù)據(jù)寫回內(nèi)存中

如果兩個(gè)線程串行執(zhí)行,此時(shí)計(jì)算結(jié)果為2。
如果兩個(gè)線程并行執(zhí)行,線程1進(jìn)行++操作到一半的時(shí)候,線程也進(jìn)行了++操作,此時(shí)自增兩次,但結(jié)果為1。
必須保證線程1save結(jié)束了,線程2再load,此時(shí)計(jì)算結(jié)果才正確
③多個(gè)線程嘗試修改同一個(gè)變量
如果是一個(gè)線程修改一個(gè)變量,線程安全
如果多個(gè)線程讀取同一個(gè)變量,線程安全
如果多個(gè)線程修改不同的變量。線程安全
④內(nèi)存可見性導(dǎo)致線程安全問題
⑤指令重排序
Java的編譯器在編譯代碼時(shí),會(huì)對(duì)指令進(jìn)行優(yōu)化,調(diào)整指令的先后順序,保證原有的邏輯不變的情況下,提高程序的運(yùn)行效率
四,解決線程安全問題
鎖-synchronized
未加鎖
static class Counter{
public int count = 0;
public void increase(){
count++;
}
}
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread t1 = new Thread(){
@Override
public void run() {
for (int i = 0; i < 50000; i++){
counter.increase();
}
}
};
Thread t2 = new Thread(){
@Override
public void run() {
for (int i = 0; i < 50000; i++){
counter.increase();
}
}
};
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(counter.count);
}



?已加鎖
static class Counter{
public int count = 0;
synchronized public void increase(){
count++;
}
}
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread t1 = new Thread(){
@Override
public void run() {
for (int i = 0; i < 50000; i++){
counter.increase();
}
}
};
Thread t2 = new Thread(){
@Override
public void run() {
for (int i = 0; i < 50000; i++){
counter.increase();
}
}
};
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(counter.count);
}


此處的synchronized就是針對(duì)counter這個(gè)對(duì)象來加鎖,進(jìn)入increase方法內(nèi)部,就把加鎖狀態(tài)設(shè)為ture,increase方法退出之后,就把加鎖狀態(tài)設(shè)為false,如果某個(gè)線程已經(jīng)把加鎖狀態(tài)設(shè)為ture,此處的其他的線程嘗試去加鎖,就會(huì)阻塞
synchronized的特性——刷新內(nèi)存
synchronized 的工作過程:
????????1. 獲得互斥鎖
????????2. 從主內(nèi)存拷貝變量的最新副本到工作的內(nèi)存
????????3. 執(zhí)行代碼
????????4. 將更改后的共享變量的值刷新到主內(nèi)存
????????5. 釋放互斥鎖
synchronized的特性——互斥
public static void main(String[] args) {
Object locker = new Object();
Thread t1 = new Thread(){
@Override
public void run() {
Scanner scanner = new Scanner(System.in);
synchronized (locker) {
System.out.println("輸入一個(gè)整數(shù)");
int num = scanner.nextInt();
System.out.println("num= " + num);
}
}
};
t1.start();
Thread t2 = new Thread(){
@Override
public void run() {
while (true){
synchronized (locker){
System.out.println("線程2獲取到鎖");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
};
t2.start();
}

一旦線程一獲取到鎖,并且沒有釋放的話,線程2就會(huì)一直在鎖這里阻塞等待
public static void main(String[] args) {
Object locker1 = new Object();
Object locker2 = new Object();
Thread t1 = new Thread(){
@Override
public void run() {
Scanner scanner = new Scanner(System.in);
synchronized (locker1) {
System.out.println("輸入一個(gè)整數(shù)");
int num = scanner.nextInt();
System.out.println("num= " + num);
}
}
};
t1.start();
Thread t2 = new Thread(){
@Override
public void run() {
while (true){
synchronized (locker2){
System.out.println("線程2獲取到鎖");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
};
t2.start();
}

?不是同一把鎖,就不回出現(xiàn)競(jìng)爭(zhēng),就沒有互斥了。
public static void main(String[] args) {
Object locker1 = new Object();
Object locker2 = new Object();
Thread t1 = new Thread(){
@Override
public void run() {
Scanner scanner = new Scanner(System.in);
synchronized (locker1.getClass()) {
System.out.println("輸入一個(gè)整數(shù)");
int num = scanner.nextInt();
System.out.println("num= " + num);
}
}
};
t1.start();
Thread t2 = new Thread(){
@Override
public void run() {
while (true){
synchronized (locker2.getClass()){
System.out.println("線程2獲取到鎖");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
};
t2.start();
}

這個(gè)代碼中,兩個(gè)線程都在針對(duì)locker1和locker2的類對(duì)象進(jìn)行競(jìng)爭(zhēng),此處的locker1和locker2的類型都是Object,對(duì)應(yīng)的對(duì)象都是相同的對(duì)象。?
到此這篇關(guān)于Java線程安全狀態(tài)專題解析的文章就介紹到這了,更多相關(guān)Java 線程安全內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java訪問者模式實(shí)現(xiàn)優(yōu)雅的對(duì)象結(jié)構(gòu)處理
Java訪問者模式是一種行為型設(shè)計(jì)模式,它通過將數(shù)據(jù)結(jié)構(gòu)和數(shù)據(jù)操作分離,實(shí)現(xiàn)對(duì)復(fù)雜對(duì)象結(jié)構(gòu)的處理。它將數(shù)據(jù)結(jié)構(gòu)中的每個(gè)元素都轉(zhuǎn)換為訪問者能夠識(shí)別的形式,從而使得數(shù)據(jù)操作可以在不影響數(shù)據(jù)結(jié)構(gòu)的前提下進(jìn)行擴(kuò)展和變化2023-04-04
Mybatis返回單個(gè)實(shí)體或者返回List的實(shí)現(xiàn)
這篇文章主要介紹了Mybatis返回單個(gè)實(shí)體或者返回List的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
Java中實(shí)現(xiàn)多重排序的幾種方法小結(jié)
Java中的多重排序通常指的是同時(shí)對(duì)一個(gè)集合中的兩個(gè)或更多列或多維度的數(shù)據(jù)進(jìn)行排序,這通常通過自定義Comparator實(shí)現(xiàn),可以結(jié)合Arrays.sort()或Collections.sort()方法,當(dāng)需要進(jìn)行多重排序時(shí),即根據(jù)多個(gè)字段進(jìn)行排序,我們可以采用以下幾種方法2024-10-10
spring security獲取用戶信息的實(shí)現(xiàn)代碼
這篇文章主要介紹了spring security獲取用戶信息的實(shí)現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
mybatis調(diào)用mysql存儲(chǔ)過程并獲取返回值方式
這篇文章主要介紹了mybatis調(diào)用mysql存儲(chǔ)過程并獲取返回值方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08

