日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

java 多线程同步_浅谈Java多线程(状态、同步等)

發(fā)布時間:2024/8/1 java 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java 多线程同步_浅谈Java多线程(状态、同步等) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Java多線程是Java程序員必須掌握的基本的知識點,這塊知識點比較復雜,知識點也比較多,今天我們一一來聊下Java多線程,系統(tǒng)的整理下這部分內(nèi)容。

一、Java中線程創(chuàng)建的三種方式:

1.通過繼承Thread類,重寫Thread的run()方法,將線程運行的邏輯放在其中

class MyThread extends Thread{

public void run(){

//do run something

}

}

public class ThreadDemo {

public static void main(String[] args) {

MyThread mt1= new MyThread();

MyThread mt2= new MyThread();

MyThread mt3= new MyThread();

mt1.start();

mt2.start();

mt3.start();

}

}

2.通過實現(xiàn)Runnable接口,實例化Thread類

//構(gòu)造一個實現(xiàn)了Runnable接口的類

class MyThread1 implements Runnable{

public void run(){

//do run something

}

}

public class RunnableDemo {

public static void main(String[] args) {

//創(chuàng)建一個類對象

MyThread1 mt = new MyThread1();

//由Runnable創(chuàng)建Thread對象

Thread t1 = new Thread(mt);

Thread t2 = new Thread(mt);

Thread t3 = new Thread(mt);

//啟動線程

t1.start();

t2.start();

t3.start();

}

}

3.實現(xiàn)Callable接口,創(chuàng)建FutureTask包裝器,實例化Thread類

FutureTask實現(xiàn)接口類圖:

public interface Future {

//取消任務的運行

boolean cancel(boolean mayInterruptIfRunning);

//如果任務在完成前取消了返回true

boolean isCancelled();

//任務結(jié)束(正常結(jié)束、中途取消、發(fā)生異常),返回true

boolean isDone();

//返回最終計算完成的結(jié)果

V get() throws InterruptedException, ExecutionException;

//返回在指定時間內(nèi)計算的結(jié)果,如果超過指定時間沒有結(jié)果則拋出TimeoutException異常

V get(long timeout, TimeUnit unit)

throws InterruptedException, ExecutionException, TimeoutException;

}

public interface Callable{

V call() throws Exception;

}

class MyThread2 implements Callable{

public Integer call(){

//do call something

}

}

public class CallableDemo{

public static void main(String[] args){

MyThread2 mt = new MyThread2();

FutureTask task = new FutureTask<>(mt);

Thread t = new Thread(task);

t.start();

Integer result = task.get();

}

}

注意:

1、不要直接調(diào)用Thread類或Runnable接口的run方法,直接調(diào)用run方法單純執(zhí)行run方法體中的內(nèi)容,而不會啟動新線程,應該調(diào)用Thread的start方法,這個方法將創(chuàng)建一個執(zhí)行run方法的新線程。

2、盡量不要使用第一種方式來創(chuàng)建線程,因為有多個任務,這種方式需要為每個任務創(chuàng)建一個獨立的線程(new MyThread()),這個代價太大。

二、未捕獲異常處理器

我們知道在run方法中無法拋出任何不可查的異常,但一旦run方法中出現(xiàn)了這類異常則會直接導致線程終止,在這種情況下,線程就GG了。通過分析,我們知道在線程死亡之前,異常會被傳遞到一個用于未捕獲異常的處理器中,所以為了防止這種情況出現(xiàn),我們可以為線程安裝一個未捕獲異常處理器。

未捕獲異常處理器必須實現(xiàn)Thread.UncaughtExceptionHanlder接口的類(這個接口類在Thread),這個類只有一個方法:

@FunctionalInterface

public interface UncaughtExceptionHandler {

void uncaughtException(Thread t, Throwable e);

}

通過Thread的靜態(tài)方法setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh)為線程安裝一個默認的處理器。

當一個線程因為未捕獲異常而終止時,通過uncaughtException方法的System.err.print打印異常信息。

public void uncaughtException(Thread t, Throwable e) {

if (parent != null) {

parent.uncaughtException(t, e);

} else {

Thread.UncaughtExceptionHandler ueh =

Thread.getDefaultUncaughtExceptionHandler();

if (ueh != null) {

ueh.uncaughtException(t, e);

} else if (!(e instanceof ThreadDeath)) {

System.err.print("Exception in thread \""

+ t.getName() + "\" ");

e.printStackTrace(System.err);

}

}

}

三、線程狀態(tài)

線程的五個基本狀態(tài):

新建(New)

可運行狀態(tài)(Runnable)

運行時狀態(tài)(Running)

阻塞狀態(tài)(Blocked)

死亡狀態(tài)(Dead)

線程調(diào)用start()方法開始后,就進入到可運行狀態(tài),隨著CPU的資源調(diào)度在運行和可運行之間切換;遇到阻塞則進入阻塞狀態(tài)。這五種狀態(tài)的相互之間轉(zhuǎn)換圖如下圖所示:

線程被阻塞可能是由于下面五方面的原因:(《Thinking in Java》)

調(diào)用sleep(毫秒數(shù)),使線程進入睡眠狀態(tài)。在規(guī)定時間內(nèi),這個線程是不會運行的。

用suspend()暫停了線程的執(zhí)行。除非收到resume()消息,否則不會返回“可運行”狀態(tài)。這兩個方法已經(jīng)過時。

用wait()暫停了線程的執(zhí)行。除非線程收到notify()或notifyAll()消息,否則不會變成“可運行”狀態(tài)。

線程正在等候一些IO操作完成。

線程試圖調(diào)用另一個對象的“同步”方法,但那個對象處于鎖定狀態(tài),暫時無法使用。

如果線程中有同步方法,那么線程狀態(tài)圖如下圖所示:

當互斥資源被一個線程訪問時,互斥資源就上鎖了,這時候其他線程訪問該互斥資源就會進入了一個鎖池(Lock pool);當鎖被釋放,其他線程獲得了鎖,就變?yōu)榭蛇\行狀態(tài)。

如果線程調(diào)用了wait()等方法,那么線程狀態(tài)圖如下圖所示:

我們都知道線程調(diào)用了wait()(這個方法是Object的方法)方法之后,線程會釋放掉鎖,這個時候線程進入等待池(Wait pool);等線程收到通知之后等待獲取鎖,獲取鎖之后才可以運行。

四、線程同步

在Java中線程同步分五種方式:

synchronized

ReentrantLock與Condition

volatile

ThreadLocal

BlockingQueue

4.1 synchronized、wait與notify

當一個線程訪問用synchronized關(guān)鍵字修飾的代碼塊,如果這個代碼塊被該線程第一個訪問,則這個線程會獲取到該Java對象的內(nèi)部鎖,其他線程訪問的時候則會因為獲取不到內(nèi)部鎖而阻塞。synchronized可以修飾類方法(static修飾的方法)、實例方法和類(Object.class),但是不能修飾抽象類的抽象方法和接口中的接口方法。

線程在執(zhí)行同步方法時是具有排它性的。當任意一個線程進入到一個對象的任意一個同步方法時,這個對象的所有同步方法都被鎖定了,在此期間,其他任何線程都不能訪問這個對象的任意一個同步方法,直到這個線程執(zhí)行完它所調(diào)用的同步方法并從中退出,從而導致它釋放了該對象的同步鎖之后。在一個對象被某個線程鎖定之后,其他線程是可以訪問這個對象的所有非同步方法的。

我們知道wait()和notify()方法只能在加鎖的代碼塊中使用,因為調(diào)用wait()方法時會釋放所持有的對象的lock,同時進入等待狀態(tài),notifyAll()方法會喚醒所有處入等待狀態(tài)的線程,注意并不是給所有喚醒線程一個對象的鎖,而是讓它們競爭。

4.2 ReentrantLock與Condition

ReentrantLock是可重入、互斥、實現(xiàn)了Lock接口的鎖,它與使用synchronized方法和快具有相同的基本行為和語義,并且擴展了其能力。其lock和unlock必須成對出現(xiàn),否則可能會出現(xiàn)死鎖,通常在finally代碼釋放鎖。ReentrantLock()還有一個可以創(chuàng)建公平鎖的構(gòu)造方法,但由于能大幅度降低程序運行效率,不推薦使用。

//fair為true時創(chuàng)建公平鎖

public ReentrantLock(boolean fair) {

sync = fair ? new FairSync() : new NonfairSync();

}

Condition代表條件對象,用于線程之間的通信,當一個線程A需要另一個線程B滿足一定條件才可以繼續(xù)操作時,A線程可以調(diào)用Condition的await()來阻塞當前線程,且放棄鎖,等到B線程執(zhí)行了某些操作并滿足了一些條件后signalAll()喚醒阻塞的線程,當A線程重新強占了鎖資源后再變成可運行狀態(tài)。Condition條件對象對于一個對象來說可以有多個,但Object的wait()和notify()對于一個對象來說只有一個條件對象。

4.3 volatile

對于volatile修飾的變量,jvm虛擬機保證從主內(nèi)存加載到線程工作內(nèi)存的值是最新的。volatile可保證變量的可見性,但無法保證原子性。

4.4 ThreadLocal

多線程同步無非是讓原本多個線程對某個操作并行變成串行,我們必須小心地對共享資源進行同步,同步不僅會帶來一定的效能延遲,而且在處理同步的時候,又要注意對象的鎖定與釋放,稍有不慎就有可能產(chǎn)生死鎖。

既然這么麻煩,ThreadLocal不對共享資源加鎖,而是為每個線程創(chuàng)造一個資源的復本。將每一個線程存取數(shù)據(jù)的行為加以隔離,實現(xiàn)的方法就是給予每個線程一個特定空間來保管該線程所獨享的資源。

當使用ThreadLocal維護變量時,ThreadLocal為每個使用該變量的線程提供獨立的變量副本,所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應的副本。從線程的角度看,目標變量就象是線程的本地變量,這也是類名中“Local”所要表達的意思。在 ThreadLocal 類中有一個 ThreadLocalMap ,用于存儲每一個線程的變量的副本。ThreadLocalMap是ThreadLocal類的一個靜態(tài)內(nèi)部類,它實現(xiàn)了鍵值對的設置和獲取(對比Map對象來理解),每個線程中都有一個獨立的ThreadLocalMap副本,它所存儲的值,只能被當前線程讀取和修改。ThreadLocal類通過操作每一個線程特有的ThreadLocalMap副本,從而實現(xiàn)了變量訪問在不同線程中的隔離。

鎖是一種以時間換空間的機制,而ThreadLocal正好是以空間換時間的。

4.5 BlockingQueue

阻塞隊列在《淺談Java集合Collection》有提到過:多線程操作共同的隊列時不需要額外的同步,另外就是隊列會自動平衡負載,即那邊(生產(chǎn)與消費兩邊)處理快了就會被阻塞掉,從而減少兩邊的處理速度差距。

參考文獻:

《Java核心技術(shù)》

總結(jié)

以上是生活随笔為你收集整理的java 多线程同步_浅谈Java多线程(状态、同步等)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。