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

詳解JUC并發(fā)編程中的進(jìn)程與線程學(xué)習(xí)

 更新時(shí)間:2022年03月24日 16:41:12   作者:夸父號(hào)  
這篇文章主要為大家詳細(xì)介紹了JUC并發(fā)編程中的進(jìn)程與線程學(xué)習(xí),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助

進(jìn)程與線程

進(jìn)程

  • 程序由指令和數(shù)據(jù)組成,但這些指令要運(yùn)行,數(shù)據(jù)要讀寫,就必須將指令加載至 CPU,數(shù)據(jù)加載至內(nèi)存。在指令運(yùn)行過(guò)程中還需要用到磁盤、網(wǎng)絡(luò)等設(shè)備。進(jìn)程就是用來(lái)加載指令、管理內(nèi)存、管理 IO 的
  • 當(dāng)一個(gè)程序被運(yùn)行,從磁盤加載這個(gè)程序的代碼至內(nèi)存,這時(shí)就開(kāi)啟了一個(gè)進(jìn)程。
  • 進(jìn)程就可以視為程序的一個(gè)實(shí)例。大部分程序可以同時(shí)運(yùn)行多個(gè)實(shí)例進(jìn)程(例如記事本、畫圖、瀏覽器等),也有的程序只能啟動(dòng)一個(gè)實(shí)例進(jìn)程(例如網(wǎng)易云音樂(lè)、360 安全衛(wèi)士等)

線程

線程是主要負(fù)責(zé)運(yùn)行指令,進(jìn)程是主要管加載指令。

一個(gè)進(jìn)程之內(nèi)可以分為一到多個(gè)線程。

一個(gè)線程就是一個(gè)指令流,將指令流中的一條條指令以一定的順序交給 CPU 執(zhí)行

Java 中,線程作為最小調(diào)度單位,進(jìn)程作為資源分配的最小單位。 在 windows 中進(jìn)程是不活動(dòng)的,只是作為線程的容器

同步異步

  • 需要等待結(jié)果返回,才能繼續(xù)運(yùn)行就是同步
  • 不需要等待結(jié)果返回,就能繼續(xù)運(yùn)行就是異步

串行并行執(zhí)行時(shí)間

使用多核cpu并行執(zhí)行可以明顯的提高執(zhí)行效率

  • 串行執(zhí)行時(shí)間 = 各個(gè)線程時(shí)間累加和 + 匯總時(shí)間
  • 并行執(zhí)行時(shí)間 = 最慢的線程時(shí)間 + 匯總時(shí)間

注意:?jiǎn)魏艘廊皇遣l(fā)的思想(即:cpu輪流去執(zhí)行線程,微觀上仍舊是串行),使用單核的多線程可能會(huì)比使用單核的單線程慢,這是因?yàn)槎嗑€程上下文切換反而浪費(fèi)了時(shí)間。

創(chuàng)建和運(yùn)行線程

1.使用 Thread

public static void main(String[] args) {
        // 創(chuàng)建線程對(duì)象
        Thread t = new Thread("線程1") {
            public void run() {
                // 要執(zhí)行的任務(wù)
                log.debug("線程1被啟動(dòng)了");
            }
        };
        // 啟動(dòng)線程
        t.start();
        log.debug("測(cè)試");
    }

2.使用 Runnable 配合 Thread

public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            public void run() {
                // 要執(zhí)行的任務(wù)
                log.debug("線程1被啟動(dòng)了");
            }
        };
        // 創(chuàng)建線程對(duì)象
        Thread t = new Thread(runnable);
        t.setName("線程1");
        // 啟動(dòng)線程
        t.start();
        log.debug("測(cè)試");
    }

這里的Runnable是一個(gè)接口,接口中只有一個(gè)抽象方法,由我們來(lái)提供實(shí)現(xiàn),實(shí)現(xiàn)中包含線程的代碼就可以了。

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

Thread 與 Runnable 的關(guān)系原理分析

方法1原理分析

方法2是使用runnable對(duì)象,當(dāng)成參數(shù)傳給Thread構(gòu)造方法,其中又調(diào)用了init方法,下面是Thread構(gòu)造方法的源碼

public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

繼續(xù)跟蹤查看runnable對(duì)象傳到哪里去了,可以看到又傳給了另一個(gè)重載的init,如下

private void init(ThreadGroup g, Runnable target, String name,                      long stackSize) {        init(g, target, name, stackSize, null, true);    }private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }

再次跟蹤可以看到是把runnable對(duì)象傳給了一個(gè)thread的一個(gè)成員變量

//省略部分代碼
this.target = target;

那么這個(gè)成員變量在哪里在使用了呢,經(jīng)過(guò)查找可以發(fā)現(xiàn)是在run方法里面,只不過(guò)Thread發(fā)現(xiàn)有runnable對(duì)象就會(huì)先采用runnable的run方法。

@Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

方法2原理分析

通過(guò)創(chuàng)建一個(gè)子類去重寫Thread類的run方法,這樣就不會(huì)執(zhí)行父類的run方法。

1.用 Runnable 更容易與線程池等高級(jí) API 配合

2.用 Runnable 讓任務(wù)類脫離了 Thread 繼承體系,更靈活

方法3 FutureTask配合Thread創(chuàng)建線程

在這里插入圖片描述

Future接口中含有g(shù)et方法來(lái)返回結(jié)果的

//省略部分代碼
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;

而runnable本身是沒(méi)有返回結(jié)果的,runnable不能將結(jié)果傳給其他線程。

public interface Runnable {
    public abstract void run();
}

要注意到FutureTask也實(shí)現(xiàn)了Runnable接口,也可以傳給Thread的有參構(gòu)造里面。

創(chuàng)建線程的代碼

public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 創(chuàng)建任務(wù)對(duì)象
        FutureTask<Integer> task3 = new FutureTask<>(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                log.debug("線程1被執(zhí)行了");
                return 666;
            }
        });
        // 參數(shù)1 是任務(wù)對(duì)象; 參數(shù)2 是線程名字,推薦
        new Thread(task3, "線程1").start();
        // 主線程阻塞,同步等待 task 執(zhí)行完畢的結(jié)果
        Integer result = task3.get();
        log.debug("結(jié)果是:{}", result);
    }

查看進(jìn)程

  • 任務(wù)管理器可以查看進(jìn)程和線程數(shù),也可以用來(lái)殺死進(jìn)程,也可以在控制臺(tái)使用tasklist查看進(jìn)程taskkill殺死進(jìn)程
  • jconsole 遠(yuǎn)程監(jiān)控配置來(lái)查看

線程運(yùn)行原理

JVM 中由堆、棧、方法區(qū)所組成,其中棧就是給線程使用的。

在這里插入圖片描述

方法調(diào)用時(shí),就會(huì)對(duì)該方法產(chǎn)生一個(gè)棧幀,方法的局部變量都會(huì)在棧幀中存儲(chǔ)。棧是后進(jìn)先出,當(dāng)method2執(zhí)行完就會(huì)回收,在執(zhí)行完同時(shí)會(huì)記錄返回地址,然后在method1中繼續(xù)執(zhí)行。

線程之間的棧幀是相互獨(dú)立的,之間互不干擾。

線程上下文切換

當(dāng)上下文切換時(shí),要保存當(dāng)前的狀態(tài),因?yàn)榭赡苁菚r(shí)間片用完了,此時(shí)線程還沒(méi)有結(jié)束。Java中對(duì)應(yīng)的就是程序計(jì)數(shù)器

start與run方法

啟動(dòng)一個(gè)線程必須要用start方法,如果直接調(diào)用類里面的run方法實(shí)際走的是main主線程。

線程start前getState()得到的是NEW

線程start后getState()得到的是RUNNABLE

public static void main(String[] args) {
        Thread t1 = new Thread("t1") {
            @Override
            public void run() {
                log.debug("t1被啟動(dòng)");
            }
        };
        System.out.println(t1.getState());
        t1.start();
        System.out.println(t1.getState());
    }

sleep方法

在sleep期間調(diào)用getState()方法可以得到TIMED_WAITING

public static void main(String[] args) {
        Thread t1 = new Thread("線程1") {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        t1.start();
        log.debug("線程1 state: {}", t1.getState());
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.debug("線程1 state: {}", t1.getState());
    }

sleep打斷

sleep可以使用interrupt方法打斷,打斷后會(huì)觸發(fā)InterruptedException異常

public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread("t1") {
            @Override
            public void run() {
                log.debug("進(jìn)入睡眠");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    log.debug("被喚醒");
                    e.printStackTrace();
                }
            }
        };
        t1.start();
        Thread.sleep(1000);
        log.debug("打斷");
        t1.interrupt();
    }

sleep防止cpu使用100%

在沒(méi)有利用cpu來(lái)計(jì)算時(shí),不要讓while(true)空轉(zhuǎn)浪費(fèi)cpu,這時(shí)可以使用yield或 sleep 來(lái)讓出cpu的使用權(quán)給其他程序

yield方法會(huì)把cpu的使用權(quán)讓出去,然后調(diào)度執(zhí)行其它線程。線程的調(diào)度最終還是依賴的操作系統(tǒng)的調(diào)度器。

join方法

該方法會(huì)等待線程的結(jié)束

static int r = 11;
    public static void main(String[] args) throws InterruptedException {
        test1();
    }
    private static void test1() throws InterruptedException {
        log.debug("主線程開(kāi)始");
        Thread t1 = new Thread(() -> {
            sleep(1);
            r = 888;
        },"線程1");
        t1.start();
//        t1.join();
        log.debug(String.valueOf(r));
        log.debug("主線程線程結(jié)束");
    }

join沒(méi)有使用時(shí),返回的是11,若是使用join返回的是888,是主線程在同步等待線程1。

當(dāng)然還有其他的方法等待

1.sleep方法等待線程1結(jié)束

2.利用FutureTask的get方法

join(long n)方法可以傳入?yún)?shù),等待線程運(yùn)行結(jié)束,最多等待 n 毫秒,假如執(zhí)行時(shí)間大于等待的時(shí)間,就會(huì)不再等待。那么該線程會(huì)直接結(jié)束嗎?答案是不會(huì)。

如下代碼

public class Test10 {
    static int r = 11;
    public static void main(String[] args) throws InterruptedException {
        test1();
    }
    private static void test1() throws InterruptedException {
        log.debug("主線程開(kāi)始");
        Thread t1 = new Thread(() -> {
            sleep(2);
            r = 888;
            log.debug("線程1結(jié)束");
        },"線程1");
        t1.start();
        t1.join(1000);
        log.debug(String.valueOf(r));
        log.debug("主線程線程結(jié)束");
    }
}

輸出結(jié)果,可以看到這里只是主線程不再等待。

16:28:53.360 c.Test10 [main] - 主線程開(kāi)始
16:28:54.411 c.Test10 [main] - 11
16:28:54.411 c.Test10 [main] - 主線程線程結(jié)束
16:28:55.404 c.Test10 [線程1] - 線程1結(jié)束

interrupt 方法

interrupt可以用來(lái)打斷處于阻塞狀態(tài)的線程。在打斷后,會(huì)有一個(gè)打斷標(biāo)記(布爾值)會(huì)提示是否被打斷過(guò),被打斷過(guò)標(biāo)記為true否則為false.
但是sleep、wait和join可以來(lái)清空打斷標(biāo)記。

代碼如下

public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            log.debug("sleep...");
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"t1");
        t1.start();
        Thread.sleep(1000);
        log.debug("interrupt");
        t1.interrupt();
        log.debug("打斷標(biāo)記:{}", t1.isInterrupted());
    }

線程被打斷后并不會(huì)結(jié)束運(yùn)行,有人就會(huì)問(wèn)了,那我們?nèi)绾卧诖驍嗑€程后關(guān)閉線程呢?答案就是利用打斷標(biāo)記去實(shí)現(xiàn)。

可以在線程的死循環(huán)之中加入一個(gè)判斷去實(shí)現(xiàn)。

boolean interrupted = Thread.currentThread().isInterrupted();
                if(interrupted) {
                    log.debug("退出循環(huán)");
                    break;
                }

守護(hù)進(jìn)程

Java 進(jìn)程通常需要所有線程都運(yùn)行結(jié)束,才會(huì)結(jié)束。

但是存在一種守護(hù)進(jìn)程,只要其他非守護(hù)進(jìn)程結(jié)束,守護(hù)進(jìn)程就會(huì)結(jié)束。垃圾回收器就使用的守護(hù)進(jìn)程。

線程的狀態(tài)

操作系統(tǒng)層面(早期進(jìn)程的狀態(tài))

在這里插入圖片描述

  • 初始狀態(tài) 在語(yǔ)言層面創(chuàng)建了線程對(duì)象,還未與操作系統(tǒng)線程關(guān)聯(lián)
  • 可運(yùn)行狀態(tài)(就緒狀態(tài))指該線程已經(jīng)被創(chuàng)建(與操作系統(tǒng)線程關(guān)聯(lián)),可以由 CPU 調(diào)度執(zhí)行任務(wù)。
  • 運(yùn)行狀態(tài) 獲取了 CPU 時(shí)間片運(yùn)行中的狀態(tài)
  • 調(diào)用阻塞api使運(yùn)行狀態(tài)轉(zhuǎn)為阻塞狀態(tài)
  • 終止?fàn)顟B(tài) 表示線程已經(jīng)執(zhí)行完畢

Java API 層面

在這里插入圖片描述

1、新建狀態(tài)(New)

Thread t1 = new Thread("t1") {
            @Override
            public void run() {
                log.debug("running...");
            }
        };
log.debug("t1 state {}", t1.getState());

2、就緒狀態(tài)(Runnable)與運(yùn)行狀態(tài)(Running)

Thread t2 = new Thread("t2") {
            @Override
            public void run() {
                while(true) { // runnable
                }
            }
        };
t2.start();
log.debug("t2 state {}", t2.getState());

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

用一個(gè)線程拿到鎖,使得當(dāng)前線程沒(méi)拿到鎖會(huì)出現(xiàn)阻塞狀態(tài)。

Thread t6 = new Thread("t6") {
            @Override
            public void run() {
                synchronized (TestState.class) { // blocked
                    try {
                        Thread.sleep(90000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
t6.start();

4、等待狀態(tài)(Waiting)

等待一個(gè)未執(zhí)行完成的線程

t2.join(); //等待狀態(tài)

5、超時(shí)等待(Time_Waiting)

可以在指定的時(shí)間自行返回的。

6、終止?fàn)顟B(tài)(TERMINATED)

線程執(zhí)行完了或者因異常退出了run()方法,該線程結(jié)束生命周期。 終止的線程不可再次復(fù)生。

總結(jié)

本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!  

相關(guān)文章

  • SpringBoot 配置文件加密的步驟

    SpringBoot 配置文件加密的步驟

    這篇文章主要介紹了SpringBoot 配置文件加密的步驟,幫助大家更好的理解和學(xué)習(xí)使用springboot框架,感興趣的朋友可以了解下
    2021-03-03
  • spring boot 注入 property的三種方式(推薦)

    spring boot 注入 property的三種方式(推薦)

    這篇文章主要介紹了spring boot 注入 property的三種方式,需要的朋友可以參考下
    2017-07-07
  • Java RabbitMQ的三種Exchange模式

    Java RabbitMQ的三種Exchange模式

    這篇文章主要介紹了Java RabbitMQ的三種Exchange模式,分別為Direct模式、Fanout模式、Topic模式,Rabbit的Direct Exchange模式是指消息發(fā)送導(dǎo)RouteKey中指定的Queue,Direct模式可以使用Rabbit自帶的Exchange
    2022-08-08
  • SpringCloud Nacos配置中心管理超詳細(xì)講解

    SpringCloud Nacos配置中心管理超詳細(xì)講解

    這篇文章主要介紹了Springcloud中的Nacos服務(wù)配置,本文以用戶微服務(wù)為例,進(jìn)行統(tǒng)一的配置,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-11-11
  • 基于spring+quartz的分布式定時(shí)任務(wù)框架實(shí)現(xiàn)

    基于spring+quartz的分布式定時(shí)任務(wù)框架實(shí)現(xiàn)

    在Spring中的定時(shí)任務(wù)功能,最好的辦法當(dāng)然是使用Quartz來(lái)實(shí)現(xiàn)。這篇文章主要介紹了基于spring+quartz的分布式定時(shí)任務(wù)框架實(shí)現(xiàn),有興趣的可以了解一下。
    2017-01-01
  • Java鏈表的天然遞歸結(jié)構(gòu)性質(zhì)圖文與實(shí)例分析

    Java鏈表的天然遞歸結(jié)構(gòu)性質(zhì)圖文與實(shí)例分析

    這篇文章主要介紹了Java鏈表的天然遞歸結(jié)構(gòu)性質(zhì),結(jié)合圖文與實(shí)例形式分析了java鏈表中遞歸操作的原理、實(shí)現(xiàn)技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2020-03-03
  • 詳解DES加密算法及在Java程序中的使用示例

    詳解DES加密算法及在Java程序中的使用示例

    這篇文章主要介紹了詳解DES加密算法及在Java程序中的使用示例,文中還有一個(gè)用Java實(shí)現(xiàn)的DES三重加密的例子,需要的朋友可以參考下
    2016-04-04
  • IDEA中的JFormDesigner使用小結(jié)

    IDEA中的JFormDesigner使用小結(jié)

    JFormDesigner是一款用于設(shè)計(jì)和創(chuàng)建圖形用戶界面的插件,本文主要介紹了IDEA中的JFormDesigner使用小結(jié),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01
  • Spring中的MultipartFile詳解

    Spring中的MultipartFile詳解

    這篇文章主要介紹了Spring中的MultipartFile詳解,隨著Spring框架的崛起,使用Spring框架中的MultipartFile來(lái)處理文件也是件很方便的事了,今天就為大家?guī)?lái)剖析MultipartFile的神秘面紗,需要的朋友可以參考下
    2024-01-01
  • 詳解SpringBoot啟動(dòng)類的掃描注解的用法及沖突原則

    詳解SpringBoot啟動(dòng)類的掃描注解的用法及沖突原則

    這篇文章主要介紹了詳解SpringBoot啟動(dòng)類的掃描注解的用法及沖突原則,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11

最新評(píng)論