大战熟女丰满人妻av-荡女精品导航-岛国aaaa级午夜福利片-岛国av动作片在线观看-岛国av无码免费无禁网站-岛国大片激情做爰视频

專注Java教育14年 全國咨詢/投訴熱線:400-8080-105
動(dòng)力節(jié)點(diǎn)LOGO圖
始于2009,口口相傳的Java黃埔軍校
首頁 hot資訊 Java多線程并發(fā)編程

Java多線程并發(fā)編程

更新時(shí)間:2021-05-17 15:54:59 來源:動(dòng)力節(jié)點(diǎn) 瀏覽1882次

1. 為什么要用多線程

小編相信所有的東西都是以實(shí)際使用價(jià)值而去學(xué)習(xí)的,沒有實(shí)際價(jià)值的學(xué)習(xí),學(xué)了沒用,沒用就不會(huì)學(xué)的好。

多線程也是一樣,以前學(xué)習(xí)Java并沒有覺得多線程有多了不起,不用多線程我一樣可以開發(fā),但是做的久了你就會(huì)發(fā)現(xiàn),一些東西必須用多線程去解決。

明白并發(fā)編程是通過cpu調(diào)度算法,讓用戶看上去同時(shí)執(zhí)行,實(shí)際上從cpu操作層面不是真正的同時(shí)。

多線程安全問題原因是在cpu執(zhí)行多線程時(shí),在執(zhí)行的過程中可能隨時(shí)切換到其他的線程上執(zhí)行。

2. 創(chuàng)建線程的方式

(1)繼承Thread類

用戶的線程類只須繼承Thread類并重寫其run()方法即可,通過調(diào)用用戶線程類的start()方法即可啟動(dòng)用戶線程

class MyThread extends Thread{
  public void run(){

  }
}

public class TestThread{
  public static void main(String[] args){
      MyThread thread = new MyThread();//創(chuàng)建用戶線程對象
      thread.start();//啟動(dòng)用戶線程
      thread.run();//主線程調(diào)用用戶線程對象的run()方法
  }
}

(2)實(shí)現(xiàn)Runnable接口

當(dāng)使用Thread(Runnable thread)方式創(chuàng)建線程對象時(shí),須為該方法傳遞一個(gè)實(shí)現(xiàn)了Runnable接口的對象,這樣創(chuàng)建的線程將調(diào)用實(shí)現(xiàn)Runnable接口的對象的run()方法

public class TestThread{
  public static void main(String[] args){
      Mythread mt = new Mythread();
      Thread t = new Thread(mt);//創(chuàng)建用戶線程
       t.start();//啟動(dòng)用戶線程
  }
}
class Mythread implements Runnable{
    public void run(){

    }
}

至于哪個(gè)好,不用說肯定是后者好,因?yàn)閷?shí)現(xiàn)接口的方式比繼承類的方式更靈活,也能減少程序之間的耦合度,面向接口編程也是設(shè)計(jì)模式6大原則的核心。

3. 線程的生命周期

4. 線程安全

指在并發(fā)的情況之下,該代碼經(jīng)過多線程使用,線程的調(diào)度順序不影響任何結(jié)果。

線程安全也是有幾個(gè)級(jí)別的:

(1)不可變

像String、Integer、Long這些,都是final類型的類,任何一個(gè)線程都改變不了它們的值,要改變除非新創(chuàng)建一個(gè),因此這些不可變對象不需要任何同步手段就可以直接在多線程環(huán)境下使用

(2)絕對線程安全

不管運(yùn)行時(shí)環(huán)境如何,調(diào)用者都不需要額外的同步措施。要做到這一點(diǎn)通常需要付出許多額外的代價(jià),Java中標(biāo)注自己是線程安全的類,實(shí)際上絕大多數(shù)都不是線程安全的,不過絕對線程安全的類,Java中也有,比方說CopyOnWriteArrayList、CopyOnWriteArraySet

(3)相對線程安全

相對線程安全也就是我們通常意義上所說的線程安全,像Vector這種,add、remove方法都是原子操作,不會(huì)被打斷,但也僅限于此,如果有個(gè)線程在遍歷某個(gè)Vector、有個(gè)線程同時(shí)在add這個(gè)Vector,99%的情況下都會(huì)出現(xiàn)ConcurrentModificationException,也就是fail-fast機(jī)制。

(4)線程非安全

這個(gè)就沒什么好說的了,ArrayList、LinkedList、HashMap等都是線程非安全的類

5. 鎖

死鎖:學(xué)習(xí)操作系統(tǒng)時(shí)給的定義:死鎖是指兩個(gè)或兩個(gè)以上的進(jìn)程在執(zhí)行過程中,由于競爭資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象,若無外力作用,它們都將無法推進(jìn)下去。此時(shí)稱系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產(chǎn)生了死鎖,這些永遠(yuǎn)在互相等待的進(jìn)程稱為死鎖進(jìn)程。

樂觀鎖:就像它的名字一樣,對于并發(fā)間操作產(chǎn)生的線程安全問題持樂觀狀態(tài),樂觀鎖認(rèn)為競爭不總是會(huì)發(fā)生,因此它不需要持有鎖,將比較-設(shè)置這兩個(gè)動(dòng)作作為一個(gè)原子操作嘗試去修改內(nèi)存中的變量,如果失敗則表示發(fā)生沖突,那么就應(yīng)該有相應(yīng)的重試邏輯。

悲觀鎖:還是像它的名字一樣,對于并發(fā)間操作產(chǎn)生的線程安全問題持悲觀狀態(tài),悲觀鎖認(rèn)為競爭總是會(huì)發(fā)生,因此每次對某資源進(jìn)行操作時(shí),都會(huì)持有一個(gè)獨(dú)占的鎖,就像synchronized,不管三七二十一,直接上了鎖就操作資源了。

6. 線程間操作

(1)線程間的通信

多個(gè)線程處理同一個(gè)資源,需要線程間通信解決線程對資源的占用,避免對同一資源爭奪。及引入等待喚醒機(jī)制(wait(),notify())

(a)wait()方法:線程調(diào)用wait()方法,釋放它對鎖的擁有權(quán),然后等待另外的線程來通知它(通知的方式是notify()或者notifyAll()方法),這樣它才能重新獲得鎖的擁有權(quán)和恢復(fù)執(zhí)行。

要確保調(diào)用wait()方法的時(shí)候擁有鎖,即,wait()方法的調(diào)用必須放在synchronized方法或synchronized塊中。

(b)notify()方法:notify()方法會(huì)喚醒一個(gè)等待當(dāng)前對象的鎖的線程。喚醒在此對象監(jiān)視器上等待的單個(gè)線程。

(c)notifAll()方法:notifyAll()方法會(huì)喚醒在此對象監(jiān)視器上等待的所有線程。

(2)兩個(gè)線程之間共享數(shù)據(jù):網(wǎng)上給出的兩種方式

方式一:當(dāng)每個(gè)線程執(zhí)行的代碼相同時(shí),可以使用同一個(gè)Runnable對象

public class MultiThreadShareData {
    public static void main(String[] args) {
        ShareData task = new ShareData(); //一個(gè)類實(shí)現(xiàn)了Runnable接口
        for(int i = 0; i < 4; i ++) {   //四個(gè)線程來賣票
            new Thread(task).start();
        }
    }
}
class ShareData implements Runnable {
    private int data = 100;
    @Override
    public void run() { //賣票,每次一個(gè)線程進(jìn)來,先判斷票數(shù)是否大于0
//      while(data > 0) {
            synchronized(this) {
                if(data > 0) {
                    System.out.println(Thread.currentThread().getName() + ": " + data);
                    data--;
                }
            }
//      }
    }
}

方式二:若每個(gè)線程執(zhí)行任務(wù)不同,可以將兩個(gè)任務(wù)方法放到一個(gè)類中,然后將data也放在這個(gè)類中,然后傳到不同的Runnable中,即可完成數(shù)據(jù)的共享

public class MultiThreadShareData {
    public static void main(String[] args) {
        ShareData task = new ShareData(); //公共數(shù)據(jù)和任務(wù)放在task中
        for(int i = 0; i < 2; i ++) { //開啟兩個(gè)線程增加data
            new Thread(new Runnable() {
                @Override
                public void run() {
                    task.increment();
                }
            }).start();
        }
        for(int i = 0; i < 2; i ++) { //開啟兩個(gè)線程減少data
            new Thread(new Runnable() {
                @Override
                public void run() {
                    task.decrement();
                }
            }).start();
        }
    }
}

class ShareData /*implements Runnable*/ {
    private int data = 0;
    public synchronized void increment() { //增加data
        System.out.println(Thread.currentThread().getName() + ": before : " + data);
        data++;
        System.out.println(Thread.currentThread().getName() + ": after : " + data);
    }
    public synchronized void decrement() { //減少data
        System.out.println(Thread.currentThread().getName() + ": before : " + data);
        data--;
        System.out.println(Thread.currentThread().getName() + ": after : " + data);
    }
}

本地線程:ThreadLocal

7. 線程池

作用:避免頻繁地創(chuàng)建和銷毀線程,達(dá)到線程對象的重用。另外,使用線程池還可以根據(jù)項(xiàng)目靈活地控制并發(fā)的數(shù)目。

(1)ThreadPoolExecutor類

1)ThreadPoolExecutor類是線程池中最核心的一個(gè)類,它提供了四個(gè)構(gòu)造方法。

public class ThreadPoolExecutor extends AbstractExecutorService {
/**
*corePoolSize:核心池的大小
*maximumPoolSize:線程池最大線程數(shù)
*keepAliveTime:表示線程沒有任務(wù)執(zhí)行時(shí)最多保持多久時(shí)間會(huì)終止
*unit:參數(shù)keepAliveTime的時(shí)間單位,有7種取值,在TimeUnit類中有7種靜態(tài)屬性
*    TimeUnit.DAYS;               //天
*    TimeUnit.HOURS;             //小時(shí)
*    TimeUnit.MINUTES;           //分鐘
*    TimeUnit.SECONDS;           //秒
*    TimeUnit.MILLISECONDS;      //毫秒
*    TimeUnit.MICROSECONDS;      //微妙
*    TimeUnit.NANOSECONDS;       //納秒
*workQueue:一個(gè)阻塞隊(duì)列,用來存儲(chǔ)等待執(zhí)行的任務(wù)
*    ArrayBlockingQueue;
*    LinkedBlockingQueue;
*    SynchronousQueue;
*threadFactory:線程工廠,主要用來創(chuàng)建線程
*handler:表示當(dāng)拒絕處理任務(wù)時(shí)的策略,有以下四種取值
*    ThreadPoolExecutor.AbortPolicy:丟棄任務(wù)并拋出RejectedExecutionException異常。
*    ThreadPoolExecutor.DiscardPolicy:也是丟棄任務(wù),但是不拋出異常。
*    ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊(duì)列最前面的任務(wù),然后重新嘗試執(zhí)行任務(wù)(重復(fù)此過程)
*    ThreadPoolExecutor.CallerRunsPolicy:由調(diào)用線程處理該任務(wù)
*/
    .....
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue);

    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);

    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler);

    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
        BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);
    ...
}

2)ThreadPoolExecutor的其他方法

a)execute()方法實(shí)際上是Executor中聲明的方法,在ThreadPoolExecutor進(jìn)行了具體的實(shí)現(xiàn),這個(gè)方法是ThreadPoolExecutor的核心方法,通過這個(gè)方法可以向線程池提交一個(gè)任務(wù),交由線程池去執(zhí)行。

b)submit()方法是在ExecutorService中聲明的方法,在AbstractExecutorService就已經(jīng)有了具體的實(shí)現(xiàn),在ThreadPoolExecutor中并沒有對其進(jìn)行重寫,這個(gè)方法也是用來向線程池提交任務(wù)的,但是它和execute()方法不同,它能夠返回任務(wù)執(zhí)行的結(jié)果,去看submit()方法的實(shí)現(xiàn),會(huì)發(fā)現(xiàn)它實(shí)際上還是調(diào)用的execute()方法,只不過它利用了Future來獲取任務(wù)執(zhí)行結(jié)果

c)shutdown()和shutdownNow()是用來關(guān)閉線程池的。

d)還有很多其他的方法:比如:getQueue()、getPoolSize()、getActiveCount()、getCompletedTaskCount()等獲取與線程池相關(guān)屬性的方法,有興趣的朋友可以自行查閱API。

(2)使用示例

使用時(shí),并不提倡直接使用ThreadPoolExcutor,而是使用Executors類中的幾個(gè)靜態(tài)方法來創(chuàng)建線程池,即

Executors.newCachedThreadPool(int  Integer.MAX_VALUE );        //創(chuàng)建一個(gè)緩沖池,緩沖池容量大小為
Executors.newSingleThreadExecutor();   //創(chuàng)建容量為1的緩沖池
Executors.newFixedThreadPool();    //創(chuàng)建固定容量大小的緩沖池

使用示例:

public class ThreadPoolTest{
    public static void main(String[] args){
        // 創(chuàng)建一個(gè)容量為5的線程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);
    // 向線程池提交一個(gè)任務(wù)(其實(shí)就是通過線程池來啟動(dòng)一個(gè)線程)
    for( int i = 0;i<15;i++){
        executorService.execute(new TestRunnable());
        system.out.println("******************");
    }
        executorService.shotdown();
    }
}
class TestRunnable extends Thread{
    @override
    public void run(){
      try{
          Thread.sleep(1000*6);
    }catch(InterruptedException e){
        e.printStackTrace();
    }
}
}

(3)其他問題

1)如果你提交任務(wù)時(shí),線程池隊(duì)列已滿,這時(shí)會(huì)發(fā)生什么

如果你使用的LinkedBlockingQueue,也就是無界隊(duì)列的話,沒關(guān)系,繼續(xù)添加任務(wù)到阻塞隊(duì)列中等待執(zhí)行,因?yàn)長inkedBlockingQueue可以近乎認(rèn)為是一個(gè)無窮大的隊(duì)列,可以無限存放任務(wù);如果你使用的是有界隊(duì)列比方說ArrayBlockingQueue的話,任務(wù)首先會(huì)被添加到ArrayBlockingQueue中,ArrayBlockingQueue滿了,則會(huì)使用拒絕策略RejectedExecutionHandler處理滿了的任務(wù),默認(rèn)是AbortPolicy。

2)高并發(fā)、任務(wù)執(zhí)行時(shí)間短的業(yè)務(wù)怎樣使用線程池?并發(fā)不高、任務(wù)執(zhí)行時(shí)間長的業(yè)務(wù)怎樣使用線程池?并發(fā)高、業(yè)務(wù)執(zhí)行時(shí)間長的業(yè)務(wù)怎樣使用線程池?這是我在并發(fā)編程網(wǎng)上看到的一個(gè)問題:

①高并發(fā)、任務(wù)執(zhí)行時(shí)間短的業(yè)務(wù),線程池線程數(shù)可以設(shè)置為CPU核數(shù)+1,減少線程上下文的切換

②并發(fā)不高、任務(wù)執(zhí)行時(shí)間長的業(yè)務(wù)要區(qū)分開看:

  • 假如是業(yè)務(wù)時(shí)間長集中在IO操作上,也就是IO密集型的任務(wù),因?yàn)镮O操作并不占用CPU,所以不要讓所有的CPU閑下來,可以加大線程池中的線程數(shù)目,讓CPU處理更多的業(yè)務(wù)
  • 假如是業(yè)務(wù)時(shí)間長集中在計(jì)算操作上,也就是計(jì)算密集型任務(wù),這個(gè)就沒辦法了,和(1)一樣吧,線程池中的線程數(shù)設(shè)置得少一些,減少線程上下文的切換

③并發(fā)高、業(yè)務(wù)執(zhí)行時(shí)間長,解決這種類型任務(wù)的關(guān)鍵不在于線程池而在于整體架構(gòu)的設(shè)計(jì),看看這些業(yè)務(wù)里面某些數(shù)據(jù)是否能做緩存是第一步,增加服務(wù)器是第二步,至于線程池的設(shè)置,設(shè)置參考2)。最后,業(yè)務(wù)執(zhí)行時(shí)間長的問題,也可能需要分析一下,看看能不能使用中間件對任務(wù)進(jìn)行拆分和解耦。

多線程的實(shí)現(xiàn)和啟動(dòng)

callable與runable區(qū)別

syncrhoized,reentrantLock各自特點(diǎn)和比對

線程池

future異步方式獲取執(zhí)行結(jié)果

concurrent包

lock

線程協(xié)作:

  • CountDownLatch:這個(gè)類是為了幫助猿友們方便的實(shí)現(xiàn)一個(gè)這樣的場景,就是某一個(gè)線程需要等待其它若干個(gè)線程完成某件事以后才能繼續(xù)進(jìn)行
  • CyclicBarrier:這個(gè)類是為了幫助猿友們方便的實(shí)現(xiàn)多個(gè)線程一起啟動(dòng)的場景,就像賽跑一樣,只要大家都準(zhǔn)備好了,那就開始一起沖。比如下面這個(gè)程序,所有的線程都準(zhǔn)備好了,才會(huì)一起開始執(zhí)行。
  • Semaphore:這個(gè)類是為了幫助猿友們方便的實(shí)現(xiàn)控制數(shù)量的場景,可以是線程數(shù)量或者任務(wù)數(shù)量等等。來看看下面這段簡單的代碼。
  • Exchanger:這個(gè)類是為了幫助猿友們方便的實(shí)現(xiàn)兩個(gè)線程交換數(shù)據(jù)的場景,使用起來非常簡單,看看下面這段代碼。

以上就是動(dòng)力節(jié)點(diǎn)小編介紹的"Java多線程并發(fā)編程",希望對大家有幫助,如有疑問,請?jiān)诰€咨詢,有專業(yè)老師隨時(shí)為您服務(wù)。

提交申請后,顧問老師會(huì)電話與您溝通安排學(xué)習(xí)

免費(fèi)課程推薦 >>
技術(shù)文檔推薦 >>
主站蜘蛛池模板: 国产日韩欧美在线观看不卡 | 老司机性视频 | 成人a毛片在线看免费全部播放 | 69视频在线观看福利视频 | 久在线视频 | 国产高清在线91福利 | 国产大战女模特在线视频 | 欧美一级毛片国产一级毛片 | 狠狠鲁狠狠操 | 色综合色综合色综合色综合网 | 欧美高清一区二区三区欧美 | 四虎影院免费网址 | 国产日产欧美 | 日韩精品中文字幕一区三区 | 免费观看精品视频999 | 天天伊人 | 色国产精品一区在线观看 | 视频一区视频二区在线观看 | 久爱www成人网免费视频 | 亚洲日日 | 天天综合网久久 | 五月婷中文字幕 | 美日韩毛片 | 国产成人综合一区人人 | 欧美亚洲网 | 久久国产乱子伦精品免费看 | 久草视频免费播放 | 国产麻豆高清视频在线第一页 | 亚洲一区二区三区精品影院 | 国产高清亚洲 | 日本α级毛片视频免费观看 | 久久久久久久久久鸭 | 欧美国产亚洲一区二区三区 | 天天干夜夜操 | 四虎免费视频 | 欧美在线一级视频 | 美女一级毛片免费不卡视频 | 免费中文字幕在线国语 | 国产大尺度福利视频在线观看 | 欧美成人一区二区三区不卡视频 | 久久久精品午夜免费不卡 |