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

詳解Java多線(xiàn)程與并發(fā)

 更新時(shí)間:2021年06月10日 11:50:53   作者:一寸HUI  
多線(xiàn)程是一個(gè)進(jìn)程在執(zhí)行過(guò)程中產(chǎn)生多個(gè)更小的程序單元,這些更小的單元稱(chēng)為線(xiàn)程,這些線(xiàn)程可以同時(shí)存在,同時(shí)運(yùn)行,一個(gè)進(jìn)程可能包含多個(gè)同時(shí)執(zhí)行的線(xiàn)程。多線(xiàn)程是實(shí)現(xiàn)并發(fā)機(jī)制的一種有效手段。進(jìn)程和線(xiàn)程一樣,都是實(shí)現(xiàn)并發(fā)的一個(gè)基本單位。

一、進(jìn)程與線(xiàn)程

進(jìn)程:是代碼在數(shù)據(jù)集合上的一次運(yùn)行活動(dòng),是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位。

線(xiàn)程:是進(jìn)程的一個(gè)執(zhí)行路徑,一個(gè)進(jìn)程中至少有一個(gè)線(xiàn)程,進(jìn)程中的多個(gè)線(xiàn)程共享進(jìn)程的 資源。

雖然系統(tǒng)是把資源分給進(jìn)程,但是CPU很特殊,是被分配到線(xiàn)程的,所以線(xiàn)程是CPU分配的基本單位。

二者關(guān)系:

一個(gè)進(jìn)程中有多個(gè)線(xiàn)程,多個(gè)線(xiàn)程共享進(jìn)程的堆和方法區(qū)資源,但是每個(gè)線(xiàn)程有自己的程序計(jì)數(shù)器和棧區(qū)域。

  • 程序計(jì)數(shù)器:是一塊內(nèi)存區(qū)域,用來(lái)記錄線(xiàn)程當(dāng)前要執(zhí)行的指令地址 。
  • 棧:用于存儲(chǔ)該線(xiàn)程的局部變量,這些局部變量是該線(xiàn)程私有的,除此之外還用來(lái)存放線(xiàn)程的調(diào)用棧禎。
  • 堆:是一個(gè)進(jìn)程中最大的一塊內(nèi)存,堆是被進(jìn)程中的所有線(xiàn)程共享的。
  • 方法區(qū):則用來(lái)存放 NM 加載的類(lèi)、常量及靜態(tài)變量等信息,也是線(xiàn)程共享的 。

二者區(qū)別:

  • 進(jìn)程:有獨(dú)立的地址空間,一個(gè)進(jìn)程崩潰后,在保護(hù)模式下不會(huì)對(duì)其它進(jìn)程產(chǎn)生影響。
  • 線(xiàn)程:是一個(gè)進(jìn)程中的不同執(zhí)行路徑。線(xiàn)程有自己的堆棧和局部變量,但線(xiàn)程之間沒(méi)有單獨(dú)的地址空間,一個(gè)線(xiàn)程死掉就等于整個(gè)進(jìn)程死掉。

1)簡(jiǎn)而言之,一個(gè)程序至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程至少有一個(gè)線(xiàn)程.

2)線(xiàn)程的劃分尺度小于進(jìn)程,使得多線(xiàn)程程序的并發(fā)性高。

3)另外,進(jìn)程在執(zhí)行過(guò)程中擁有獨(dú)立的內(nèi)存單元,而多個(gè)線(xiàn)程共享內(nèi)存,從而極大地提高了程序的運(yùn)行效率。

4)每個(gè)獨(dú)立的線(xiàn)程有一個(gè)程序運(yùn)行的入口、順序執(zhí)行序列和程序的出口。但是線(xiàn)程不能夠獨(dú)立執(zhí)行,必須依存在應(yīng)用程序中,由應(yīng)用程序提供多個(gè)線(xiàn)程執(zhí)行控制。

5)從邏輯角度來(lái)看,多線(xiàn)程的意義在于一個(gè)應(yīng)用程序中,有多個(gè)執(zhí)行部分可以同時(shí)執(zhí)行。但操作系統(tǒng)并沒(méi)有將多個(gè)線(xiàn)程看做多個(gè)獨(dú)立的應(yīng)用,來(lái)實(shí)現(xiàn)進(jìn)程的調(diào)度和管理以及資源分配。這就是進(jìn)程和線(xiàn)程的重要區(qū)別

二、并發(fā)與并行

并發(fā):是指同一個(gè)時(shí)間段內(nèi)多個(gè)任務(wù)同時(shí)都在執(zhí)行,并且都沒(méi)有執(zhí)行結(jié)束。并發(fā)任務(wù)強(qiáng)調(diào)在一個(gè)時(shí)間段內(nèi)同時(shí)執(zhí)行,而一個(gè)時(shí)間段由多個(gè)單位時(shí)間累積而成,所以說(shuō)并發(fā)的多個(gè)任務(wù)在單位時(shí)間內(nèi)不一定同時(shí)在執(zhí)行 。

并行:是說(shuō)在單位時(shí)間內(nèi)多個(gè)任務(wù)同時(shí)在執(zhí)行 。

在多線(xiàn)程編程實(shí)踐中,線(xiàn)程的個(gè)數(shù)往往多于CPU的個(gè)數(shù),所以一般都稱(chēng)多線(xiàn)程并發(fā)編程而不是多線(xiàn)程并行編程。

并發(fā)過(guò)程中常見(jiàn)的問(wèn)題:

1、線(xiàn)程安全問(wèn)題

多個(gè)線(xiàn)程同時(shí)操作共享變量1時(shí),會(huì)出現(xiàn)線(xiàn)程1更新共享變量1的值,但是其他線(xiàn)程獲取到的是共享變量沒(méi)有被更新之前的值。就會(huì)導(dǎo)致數(shù)據(jù)不準(zhǔn)確問(wèn)題。

2、共享內(nèi)存不可見(jiàn)性問(wèn)題

Java內(nèi)存模型(處理共享變量)

Java 內(nèi)存模型規(guī)定,將所有的變量都存放在主內(nèi)存中,當(dāng)線(xiàn)程使用變量時(shí),會(huì)把主內(nèi)存里面的變量復(fù)制到自己的工作空間或者叫作工作內(nèi)存,線(xiàn)程讀寫(xiě)變量時(shí)操作的是自己工作內(nèi)存中的變量 。(如上圖所示)

(實(shí)際工作的java內(nèi)存模型)

上圖中所示是一個(gè)雙核 CPU 系統(tǒng)架構(gòu),每個(gè)核有自己的控制器和運(yùn)算器,其中控制器包含一組寄存器和操作控制器,運(yùn)算器執(zhí)行算術(shù)邏輔運(yùn)算。CPU的每個(gè)核都有自己的一級(jí)緩存,在有些架構(gòu)里面還有一個(gè)所有CPU都共享的二級(jí)緩存。 那么Java內(nèi)存模型里面的工作內(nèi)存,就對(duì)應(yīng)這里的 Ll或者 L2 緩存或者 CPU 的寄存器

1、線(xiàn)程A首先獲取共享變量X的值,由于兩級(jí)Cache都沒(méi)有命中,所以加載主內(nèi)存中X的值,假如為0。然后把X=0的值緩存到兩級(jí)緩存,線(xiàn)程A修改X的值為1,然后將其寫(xiě)入兩級(jí)Cache,并且刷新到主內(nèi)存。線(xiàn)程A操作完畢后,線(xiàn)程A所在的CPU的兩級(jí)Cache內(nèi)和主內(nèi)存里面的X的值都是l。

2、線(xiàn)程B獲取X的值,首先一級(jí)緩存沒(méi)有命中,然后看二級(jí)緩存,二級(jí)緩存命中了,所以返回X=1;到這里一切都是正常的,因?yàn)檫@時(shí)候主內(nèi)存中也是X=l。然后線(xiàn)程B修改X的值為2,并將其存放到線(xiàn)程2所在的一級(jí)Cache和共享二級(jí)Cache中,最后更新主內(nèi)存中X的值為2,到這里一切都是好的。

3、線(xiàn)程A這次又需要修改X的值,獲取時(shí)一級(jí)緩存命中,并且X=l這里問(wèn)題就出現(xiàn)了,明明線(xiàn)程B已經(jīng)把X的值修改為2,為何線(xiàn)程A獲取的還是l呢?這就是共享變量的內(nèi)存不可見(jiàn)問(wèn)題,也就是線(xiàn)程B寫(xiě)入的值對(duì)線(xiàn)程A不可見(jiàn)。

synchronized 的內(nèi)存語(yǔ)義:

這個(gè)內(nèi)存語(yǔ)義就可以解決共享變量?jī)?nèi)存可見(jiàn)性問(wèn)題。進(jìn)入synchronized塊的內(nèi)存語(yǔ)義是把在synchronized塊內(nèi)使用到的變量從線(xiàn)程的工作內(nèi)存中清除,這樣在synchronized塊內(nèi)使用到該變量時(shí)就不會(huì)從線(xiàn)程的工作內(nèi)存中獲取,而是直接從主內(nèi)存中獲取。退出synchronized塊的內(nèi)存語(yǔ)義是把在synchronized塊內(nèi)對(duì)共享變量的修改刷新到主內(nèi)存。會(huì)造成上下文切換的開(kāi)銷(xiāo),獨(dú)占鎖,降低并發(fā)性

Volatile的理解:

該關(guān)鍵字可以確保對(duì)一個(gè)變量的更新對(duì)其他線(xiàn)程馬上可見(jiàn)。當(dāng)一個(gè)變量被聲明為volatile時(shí),線(xiàn)程在寫(xiě)入變量時(shí)不會(huì)把值緩存在寄存器或者其他地方,而是會(huì)把值刷新回主內(nèi)存。當(dāng)其他線(xiàn)程讀取該共享變量時(shí)-,會(huì)從主內(nèi)存重新獲取最新值,而不是使用當(dāng)前線(xiàn)程的工作內(nèi)存中的值。volatile的內(nèi)存語(yǔ)義和synchronized有相似之處,具體來(lái)說(shuō)就是,當(dāng)線(xiàn)程寫(xiě)入了volatile變量值時(shí)就等價(jià)于線(xiàn)程退出synchronized同步塊(把寫(xiě)入工作內(nèi)存的變量值同步到主內(nèi)存),讀取volatile變量值時(shí)就相當(dāng)于進(jìn)入同步塊(先清空本地內(nèi)存變量值,再?gòu)闹鲀?nèi)存獲取最新值)。不能保證原子性

三、創(chuàng)建線(xiàn)程

1、繼承Thread類(lèi)

重寫(xiě)run方法:使用繼承方式的好處是,在run()方法內(nèi)獲取當(dāng)前線(xiàn)程直接使用this就可以了,無(wú)須使用Thread.currentThread()方法;不好的地方是Java不支持多繼承,如果繼承了Thread類(lèi),那么就不能再繼承其他類(lèi)。另外任務(wù)與代碼沒(méi)有分離,當(dāng)多個(gè)線(xiàn)程執(zhí)行一樣的任務(wù)時(shí)需要多份任務(wù)代碼。

public class ThreadRuning extends Thread{

    public ThreadRuning(String name){  
//重寫(xiě)構(gòu)造,可以對(duì)線(xiàn)程添加名字
        super(name);
    }
    @Override
    public void run() {
        while(true){
            System.out.println("good time");
//在run方法里,this代表當(dāng)前線(xiàn)程
            System.out.println(this);
        }
    }
    public static void main(String[] args){
        ThreadRuning threadRuning = new ThreadRuning("1111");
        threadRuning.start();
    }
}

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

實(shí)現(xiàn)run方法:解決繼承Thread的缺點(diǎn),沒(méi)有返回值

public class RunableTest implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.println("good time");
        }
    }
    public static void main(String[] args) {
        RunableTest runableTest1 = new RunableTest();
        RunableTest runableTest2 = new RunableTest();
        new Thread(runableTest1).start();
        new Thread(runableTest1).start();
        new Thread(runableTest2).start();
    }
}

3、實(shí)現(xiàn)Callable接口

實(shí)現(xiàn)call方法:

public class CallTest implements Callable {
    @Override
    public Object call() throws Exception {
        return "hello world";
    }
 
    public static void main(String[] args){
        FutureTask<String> futureTask = new FutureTask<String>(new CallTest());
        new Thread(futureTask).start();
        try {
            String result = futureTask.get();
            System.out.println(result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

使用繼承方式的好處是方便傳參,你可以在子類(lèi)里面添加成員變量,通過(guò)set方法設(shè)置參數(shù)或者通過(guò)構(gòu)造函數(shù)進(jìn)行傳遞,而如果使用Runnable方式,則只能使用主線(xiàn)程里面被聲明為final的變量。不好的地方是Java不支持多繼承,如果繼承了Thread類(lèi),那么子類(lèi)不能再繼承其他類(lèi),而Runable則沒(méi)有這個(gè)限制。前兩種方式都沒(méi)辦法拿到任務(wù)的返回結(jié)果,但是Callable方式可以

四、Thread類(lèi)詳解

1、線(xiàn)程特性

1、線(xiàn)程能被標(biāo)記為守護(hù)線(xiàn)程,也可以是用戶(hù)線(xiàn)程

2、每個(gè)線(xiàn)程均分配一個(gè)name,默認(rèn)為(Thread-自增數(shù)字)的組合

3、每個(gè)線(xiàn)程都有優(yōu)先級(jí).高優(yōu)先級(jí)線(xiàn)程優(yōu)先于低優(yōu)先級(jí)線(xiàn)程執(zhí)行. 1-10,默認(rèn)為5

4、main所在的線(xiàn)程組為main,構(gòu)造線(xiàn)程的時(shí)候沒(méi)有現(xiàn)實(shí)的指定線(xiàn)程組,線(xiàn)程組默認(rèn)和父線(xiàn)程一樣

5、當(dāng)線(xiàn)程中的run()方法代碼里面又創(chuàng)建了一個(gè)新的線(xiàn)程對(duì)象時(shí),新創(chuàng)建的線(xiàn)程優(yōu)先級(jí)和父線(xiàn)程優(yōu)先級(jí)一樣.

6、當(dāng)且僅當(dāng)父線(xiàn)程為守護(hù)線(xiàn)程時(shí),新創(chuàng)建的線(xiàn)程才會(huì)是守護(hù)線(xiàn)程.

7、當(dāng)JVM啟動(dòng)時(shí),通常會(huì)有唯一的一個(gè)非守護(hù)線(xiàn)程(這一線(xiàn)程用于調(diào)用指定類(lèi)的main()方法)

JVM會(huì)持續(xù)執(zhí)行線(xiàn)程直到下面情況某一個(gè)發(fā)生為止:

1)類(lèi)運(yùn)行時(shí)exit()方法被調(diào)用 且 安全機(jī)制允許此exit()方法的調(diào)用.

2)所有非守護(hù)類(lèi)型的線(xiàn)程均已經(jīng)終止,or run()方法調(diào)用返回or在run()方法外部拋出了一些可傳播性的異常.

2、Init方法

/**
 * Initializes a Thread.
 * @param g 線(xiàn)程組
 * @param target 執(zhí)行對(duì)象
 * @param name 線(xiàn)程名
 * @param stackSize 新線(xiàn)程棧大小,為0表示忽略
 * @param acc用于繼承的訪(fǎng)問(wèn)控制上下文
 * @param inheritThreadLocals如果值為true,從構(gòu)造線(xiàn)程繼承可繼承線(xiàn)程局部變量的初始值
*/
private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals) {
    if (name == null) {
        throw new NullPointerException("name cannot be null");
    }
    this.name = name;
    Thread parent = currentThread();
    SecurityManager security = System.getSecurityManager();
   //如果所屬線(xiàn)程組為null
if (g == null) {
        /* Determine if it's an applet or not */
 
        /* If there is a security manager, ask the security manager
   //如果有安全管理,查詢(xún)安全管理需要做的工作
           what to do. */
        if (security != null) {
            g = security.getThreadGroup();
        }
 
        /* If the security doesn't have a strong opinion of the matter
           use the parent thread group. */
//如果安全管理在線(xiàn)程所屬父線(xiàn)程組的問(wèn)題上沒(méi)有什么強(qiáng)制的要求
        if (g == null) {
            g = parent.getThreadGroup();
        }
    }
 
    /* checkAccess regardless of whether or not threadgroup is
       explicitly passed in. */
//無(wú)論所屬線(xiàn)程組是否顯示傳入,都要進(jìn)行檢查訪(fǎng)問(wèn).
    g.checkAccess();
    /*
     * Do we have the required permissions?
     */
    if (security != null) {
        if (isCCLOverridden(getClass())) {
            security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
        }
    }
    g.addUnstarted();
    this.group = g;
    this.daemon = parent.isDaemon();//如果父線(xiàn)程為守護(hù)線(xiàn)程,則此線(xiàn)程也被 設(shè)置為守護(hù)線(xiàn)程.
    this.priority = parent.getPriority();//獲取父進(jìn)程的優(yōu)先級(jí)
    if (security == null || isCCLOverridden(parent.getClass()))
        this.contextClassLoader = parent.getContextClassLoader();
    else
        this.contextClassLoader = parent.contextClassLoader;
    this.inheritedAccessControlContext =
            acc != null ? acc : AccessController.getContext();
    this.target = target;
    setPriority(priority);
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals =
            ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    /* Stash the specified stack size in case the VM cares */
    this.stackSize = stackSize;
 
    /* Set thread ID 設(shè)置線(xiàn)程id*/
    tid = nextThreadID();
}

3、構(gòu)造方法

所有的構(gòu)造方法都是調(diào)用init()方法

public Thread() {
    init(null, null, "Thread-" + nextThreadNum(), 0);
}
 
public Thread(Runnable target) {
    init(null, target, "Thread-" + nextThreadNum(), 0);
}
 
 
public Thread(Runnable target, AccessControlContext acc) {
    init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);
}
 
public Thread(ThreadGroup group, Runnable target) {
    init(group, target, "Thread-" + nextThreadNum(), 0);
}
 
 
public Thread(String name) {
    init(null, null, name, 0);
}
 
 
public Thread(ThreadGroup group, String name) {
    init(group, null, name, 0);
}
 
 
public Thread(Runnable target, String name) {
    init(null, target, name, 0);
}
 
 
public Thread(ThreadGroup group, Runnable target, String name,
              long stackSize) {
    init(group, target, name, stackSize);
}

4、線(xiàn)程狀態(tài)

public enum State {
    NEW,
    RUNNABLE,
    BLOCKED,
    WAITING,
    TIMED_WAITING,
    TERMINATED;
}

NEW:狀態(tài)是指線(xiàn)程剛創(chuàng)建, 尚未啟動(dòng)

RUNNABLE:狀態(tài)是線(xiàn)程正在正常運(yùn)行中, 當(dāng)然可能會(huì)有某種耗時(shí)計(jì)算/IO等待的操作/CPU時(shí)間片切換等, 這個(gè)狀態(tài)下發(fā)生的等待一般是其他系統(tǒng)資源, 而不是鎖, Sleep等

BLOCKED:這個(gè)狀態(tài)下, 是在多個(gè)線(xiàn)程有同步操作的場(chǎng)景, 比如正在等待另一個(gè)線(xiàn)程的synchronized 塊的執(zhí)行釋放, 或者可重入的 synchronized塊里別人調(diào)用wait() 方法, 也就是這里是線(xiàn)程在等待進(jìn)入臨界區(qū)

WAITING:這個(gè)狀態(tài)下是指線(xiàn)程擁有了某個(gè)鎖之后, 調(diào)用了他的wait方法, 等待其他線(xiàn)程/鎖擁有者調(diào)用 notify / notifyAll 一遍該線(xiàn)程可以繼續(xù)下一步操作, 這里要區(qū)分 BLOCKED 和 WATING 的區(qū)別, 一個(gè)是在臨界點(diǎn)外面等待進(jìn)入, 一個(gè)是在理解點(diǎn)里面wait等待別人notify, 線(xiàn)程調(diào)用了join方法 join了另外的線(xiàn)程的時(shí)候, 也會(huì)進(jìn)入WAITING狀態(tài), 等待被他join的線(xiàn)程執(zhí)行結(jié)束

TIMED_WAITING:這個(gè)狀態(tài)就是有限的(時(shí)間限制)的WAITING, 一般出現(xiàn)在調(diào)用wait(long), join(long)等情況下, 另外一個(gè)線(xiàn)程sleep后, 也會(huì)進(jìn)入TIMED_WAITING狀態(tài)

TERMINATED:這個(gè)狀態(tài)下表示 該線(xiàn)程的run方法已經(jīng)執(zhí)行完畢了, 基本上就等于死亡了(當(dāng)時(shí)如果線(xiàn)程被持久持有, 可能不會(huì)被回收)

(在很多文章中都寫(xiě)了running狀態(tài),其實(shí)源碼里面只有六種的,當(dāng)自己寫(xiě)一個(gè)線(xiàn)程通過(guò)while一直保持執(zhí)行狀態(tài),然后使用jconsole工具去查看線(xiàn)程的狀態(tài),確實(shí)是Runable狀態(tài))

Api文檔是這么說(shuō)的:

其實(shí)我們可以理解為兩種狀態(tài),一個(gè)是running,表示正在執(zhí)行,一個(gè)是runable,表示準(zhǔn)備就緒了,只是在等待其他的系統(tǒng)資源。然后我們就可以理解如下圖

5、Start方法

public synchronized void start() {
    /**
        * 此方法并不會(huì)被主要方法線(xiàn)程or由虛擬機(jī)創(chuàng)建的系統(tǒng)組線(xiàn)程所調(diào)用.
        * 任何向此方法添加的新功能方法在未來(lái)都會(huì)被添加到虛擬機(jī)中.
        * 0狀態(tài)值代表了NEW的狀態(tài).
        */
    if (threadStatus != 0) // 線(xiàn)程不能重復(fù)start
        throw new IllegalThreadStateException();

    /* Notify the group that this thread is about to be started
        * so that it can be added to the group's list of threads
        * and the group's unstarted count can be decremented. */
    group.add(this);

    boolean started = false;
    try {
        start0(); //本地方法
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
                it will be passed up the call stack */
        }
    }
}

private native void start0();

6、yield方法

public static native void yield();

是一個(gè)本地方法,提示線(xiàn)程調(diào)度器當(dāng)前線(xiàn)程愿意放棄當(dāng)前CPU的使用。如果當(dāng)前資源不緊張,調(diào)度器可以忽略這個(gè)提示。本質(zhì)上線(xiàn)程狀態(tài)一直是RUNNABLE,但是我可以理解為RUNNABLE到RUNNING的轉(zhuǎn)換

7、sleep方法

/**
     * 此方法會(huì)引起當(dāng)前執(zhí)行線(xiàn)程sleep(臨時(shí)停止執(zhí)行)指定毫秒數(shù).
     * 此方法的調(diào)用不會(huì)引起當(dāng)前線(xiàn)程放棄任何監(jiān)聽(tīng)器(monitor)的所有權(quán)(ownership).
     */
public static native void sleep(long millis) throws InterruptedException;
 
public static void sleep(long millis, int nanos)
throws InterruptedException {
    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }
 
    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException(
                            "nanosecond timeout value out of range");
    }
 
    if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
        millis++;
    }
 
    sleep(millis);
}

sleep方法,有一個(gè)重載方法,sleep方法會(huì)釋放cpu的時(shí)間片,但是不會(huì)釋放鎖,調(diào)用sleep()之后從RUNNABLE狀態(tài)轉(zhuǎn)為T(mén)IMED_WAITING狀態(tài)

8、join方法

/**
    * 最多等待參數(shù)millis(ms)時(shí)長(zhǎng)當(dāng)前線(xiàn)程就會(huì)死亡.參數(shù)為0時(shí)則要持續(xù)等待.
    * 此方法在實(shí)現(xiàn)上:循環(huán)調(diào)用以this.isAlive()方法為條件的wait()方法.
    * 當(dāng)線(xiàn)程終止時(shí)notifyAll()方法會(huì)被調(diào)用.
    * 建議應(yīng)用程序不要在線(xiàn)程實(shí)例上使用wait,notify,notifyAll方法.
    */
public final synchronized void join(long millis)
        throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;

    //如果等待時(shí)間<0,則拋出異常
    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    //如果等待時(shí)間為0
    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

//等待時(shí)間單位為納秒,其它解釋都和上面方法一樣
public final synchronized void join(long millis, int nanos)
        throws InterruptedException {

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException(
                "nanosecond timeout value out of range");
    }

    if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
        millis++;
    }

    join(millis);
}

//方法功能:等待一直到線(xiàn)程死亡.
public final void join() throws InterruptedException {
    join(0);
}

join某個(gè)線(xiàn)程A,會(huì)使得線(xiàn)程B進(jìn)入等待,知道線(xiàn)程A結(jié)束,或者到達(dá)給定的時(shí)間,那么期間線(xiàn)程B處于BLOCKED的狀態(tài),而不是線(xiàn)程A

五、其他方法

接下來(lái)聊一下Object類(lèi)的wait,notify和notifyAll方法

1、wait方法

public final native void wait(long timeout) throws InterruptedException; //本地方法 參數(shù)為毫秒
public final void wait(long timeout, int nanos) throws InterruptedException {//參數(shù)為毫秒和納秒
    if (timeout < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException(
                            "nanosecond timeout value out of range");
    }

    if (nanos > 0) {
        timeout++;
    }

    wait(timeout);
}
public final void wait() throws InterruptedException {
    wait(0);
}

可見(jiàn)wait()和wait(long timeout, int nanos)都在在內(nèi)部調(diào)用了wait(long timeout)方法。
下面主要是說(shuō)說(shuō)wait(long timeout)方法

wait方法會(huì)引起當(dāng)前線(xiàn)程阻塞,直到另外一個(gè)線(xiàn)程在對(duì)應(yīng)的對(duì)象上調(diào)用notify或者notifyAll()方法,或者達(dá)到了方法參數(shù)中指定的時(shí)間。

調(diào)用wait方法的當(dāng)前線(xiàn)程一定要擁有對(duì)象的監(jiān)視器鎖。

wait方法會(huì)把當(dāng)前線(xiàn)程T放置在對(duì)應(yīng)的object上的等待隊(duì)列中,在這個(gè)對(duì)象上的所有同步請(qǐng)求都不會(huì)得到響應(yīng)。線(xiàn)程調(diào)度將不會(huì)調(diào)用線(xiàn)程T,在以下四件事發(fā)生之前,線(xiàn)程T會(huì)被喚醒(線(xiàn)程T是在其代碼中調(diào)用wait方法的那個(gè)線(xiàn)程)

1、當(dāng)其他的線(xiàn)程在對(duì)應(yīng)的對(duì)象上調(diào)用notify方法,而在此對(duì)象的對(duì)應(yīng)的等待隊(duì)列中將會(huì)任意選擇一個(gè)線(xiàn)程進(jìn)行喚醒。

2、其他的線(xiàn)程在此對(duì)象上調(diào)用了notifyAll方法

3、其他的線(xiàn)程調(diào)用了interrupt方法來(lái)中斷線(xiàn)程T

4、等待的時(shí)間已經(jīng)超過(guò)了wait中指定的時(shí)間。如果參數(shù)timeout的值為0,不是指真實(shí)的等待時(shí)間是0,而是線(xiàn)程等待直到被另外一個(gè)線(xiàn)程喚醒為止。

被喚醒的線(xiàn)程T會(huì)被從對(duì)象的等待隊(duì)列中移除并且重新能夠被線(xiàn)程調(diào)度器調(diào)度。之后,線(xiàn)程T會(huì)像平常一樣跟其他的線(xiàn)程競(jìng)爭(zhēng)獲取對(duì)象上的鎖;一旦線(xiàn)程T獲得了此對(duì)象上的鎖,那么在此對(duì)象上的所有同步請(qǐng)求都會(huì)恢復(fù)到之前的狀態(tài),也就是恢復(fù)到wait被調(diào)用的情況下。然后線(xiàn)程T從wait方法的調(diào)用中返回。因此,當(dāng)從wait方法返回時(shí),對(duì)象的狀態(tài)以及線(xiàn)程T的狀態(tài)跟wait方法被調(diào)用的時(shí)候一樣。

線(xiàn)程在沒(méi)有被喚醒,中斷或者時(shí)間耗盡的情況下仍然能夠被喚醒,這叫做偽喚醒。雖然在實(shí)際中,這種情況很少發(fā)生,但是程序一定要測(cè)試這個(gè)能夠喚醒線(xiàn)程的條件,并且在條件不滿(mǎn)足時(shí),線(xiàn)程繼續(xù)等待。換言之,wait操作總是出現(xiàn)在循環(huán)中,就像下面這樣:

synchronized(對(duì)象){
    while(條件不滿(mǎn)足){
     對(duì)象.wait();
  }
  對(duì)應(yīng)的邏輯處理
}

如果當(dāng)前的線(xiàn)程被其他的線(xiàn)程在當(dāng)前線(xiàn)程等待之前或者正在等待時(shí)調(diào)用了interrupt()中斷了,那么會(huì)拋出InterruptedExcaption異常。直到這個(gè)對(duì)象上面的鎖狀態(tài)恢復(fù)到上面描述的狀態(tài)以前,這個(gè)異常是不會(huì)拋出的。

要注意的是,wait方法把當(dāng)前線(xiàn)程放置到這個(gè)對(duì)象的等待隊(duì)列中,解鎖也僅僅是在這個(gè)對(duì)象上;當(dāng)前線(xiàn)程在其他對(duì)象上面上的鎖在當(dāng)前線(xiàn)程等待的過(guò)程中仍然持有其他對(duì)象的鎖。

這個(gè)方法應(yīng)該僅僅被持有對(duì)象監(jiān)視器的線(xiàn)程調(diào)用。 wait(long timeout, int nanos)方法的實(shí)現(xiàn)中只要nanos大于0,那么timeout時(shí)間就加上一毫秒,主要是更精確的控制時(shí)間,其他的跟wait(long timeout)一樣

2、notify方法

public final native void notify(); //本地方法

通知可能等待該對(duì)象的對(duì)象鎖的其他線(xiàn)程。由JVM(與優(yōu)先級(jí)無(wú)關(guān))隨機(jī)挑選一個(gè)處于wait狀態(tài)的線(xiàn)程。
在調(diào)用notify()之前,線(xiàn)程必須獲得該對(duì)象的對(duì)象級(jí)別鎖
執(zhí)行完notify()方法后,不會(huì)馬上釋放鎖,要直到退出synchronized代碼塊,當(dāng)前線(xiàn)程才會(huì)釋放鎖
notify()一次只隨機(jī)通知一個(gè)線(xiàn)程進(jìn)行喚醒

3、notifyAll()方法

public final native void notifyAll();//本地方法

和notify()差不多,只不過(guò)是使所有正在等待池中等待同一共享資源的全部線(xiàn)程從等待狀態(tài)退出,進(jìn)入可運(yùn)行狀態(tài)
讓它們競(jìng)爭(zhēng)對(duì)象的鎖,只有獲得鎖的線(xiàn)程才能進(jìn)入就緒狀態(tài)

每個(gè)鎖對(duì)象有兩個(gè)隊(duì)列:就緒隊(duì)列和阻塞隊(duì)列

- 就緒隊(duì)列:存儲(chǔ)將要獲得鎖的線(xiàn)程

- 阻塞隊(duì)列:存儲(chǔ)被阻塞的線(xiàn)程

六、實(shí)例

1、sleep

public class ThreadDemo1 {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
 
        //推薦
        MyRunnable mr = new MyRunnable();
        Thread t2 = new Thread(mr);
 
        mt.start();//啟動(dòng)線(xiàn)程
        t2.start();
 
 
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "-" + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
 
    }
}
 
/**
 * 實(shí)現(xiàn)線(xiàn)程的第一種方式:繼承thread類(lèi)
 */
class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (this.isInterrupted()) {
                break;
            }
            System.out.println(Thread.currentThread().getName() + "-" + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
                this.interrupt();
            }
 
        }
    }
}
 
/**
 * 實(shí)現(xiàn)線(xiàn)程的第二種方式:實(shí)現(xiàn)Runnable接口
 */
class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "-" + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

2、join和中斷(推薦用標(biāo)記中斷)

public class ThreadDemo2 {
 
    public static void main(String[] args){
 
        MyRunable2 mr2 = new MyRunable2();
        Thread t = new Thread(mr2);
//        t.start();
 
        MyRunable3 mr3 = new MyRunable3();
        Thread t2 = new Thread(mr3);
        t2.start();
 
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName()+"--"+i);
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(i==20){
//                try {  //這些打開(kāi)用來(lái)測(cè)試join
//                    t.join();//讓t線(xiàn)程執(zhí)行完畢
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//                t.interrupt();//中斷線(xiàn)程,只是作了一個(gè)中斷標(biāo)記,用于測(cè)試interrupt方法
                mr3.flag = false; //用于測(cè)試標(biāo)記中斷
            }
        }
    }
}
 
class MyRunable2 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            if(Thread.interrupted()){//測(cè)試中斷狀態(tài),此方法會(huì)把中斷狀態(tài)清除
                //....
                break;
            }
            System.out.println(Thread.currentThread().getName()+"--"+i);
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
                Thread.currentThread().interrupt();
            }
        }
    }
}
<br>//標(biāo)記中斷
class MyRunable3 implements Runnable{
    public boolean flag = true;
    public MyRunable3(){
        flag = true;
    }
    @Override
    public void run() {
        int i=0;
        while(flag){
            System.out.println(Thread.currentThread().getName()+"==="+(i++));
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

3、優(yōu)先級(jí)和守護(hù)進(jìn)程

public class ThreadDemo3 {
    public static void main(String[] args){
        MyRunnable4 mr4 = new MyRunnable4();
        Thread t = new Thread(mr4);
        t.setName("Thread-t");
        //優(yōu)先級(jí)高可以提高該線(xiàn)程搶點(diǎn)CPU時(shí)間片的概率大
        t.setPriority(Thread.MAX_PRIORITY);
        //線(xiàn)程可以分成守護(hù)線(xiàn)程和 用戶(hù)線(xiàn)程,當(dāng)進(jìn)程中沒(méi)有用戶(hù)線(xiàn)程時(shí),JVM會(huì)退出
        t.setDaemon(true);//把線(xiàn)程設(shè)置為守護(hù)線(xiàn)程
        System.out.println(t.isAlive());
        t.start();
        System.out.println(t.isAlive());
 
        for (int i = 0; i < 50; i++) {
            System.out.println("main--"+i);
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (i==5){
                Thread.yield();//讓出本次CPU執(zhí)行時(shí)間片
            }
        }
    }
}
class MyRunnable4 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println("--"+i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

4、生產(chǎn)者與消費(fèi)者

定義一個(gè)接口:

package threadtest.procon;
 
public interface AbstractStorage {
    void consume(int num);
    void product(int num);
}

定義一個(gè)類(lèi)實(shí)現(xiàn)接口,用于存放生產(chǎn)的東西

package threadtest.procon;
import java.util.LinkedList;
public class Storage implements AbstractStorage{
    private final int MAX_SIZE = 100;
    private LinkedList list = new LinkedList();
 
    @Override
    public void consume(int num) {
        synchronized (list){
            while (list.size()<num){
                System.out.println("【要消費(fèi)的產(chǎn)品數(shù)量】:" + num + "\t【庫(kù)存量】:"+ list.size() + "\t暫時(shí)不能執(zhí)行消費(fèi)任務(wù)!");
                try {
                    list.wait(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
 
            for(int i=0;i<num;i++){
                list.remove();
            }
            System.out.println("【已經(jīng)消費(fèi)產(chǎn)品數(shù)】:" + num + "\t【現(xiàn)倉(cāng)儲(chǔ)量為】:" + list.size());
 
            list.notifyAll();
        }
    }
 
    @Override
    public void product(int num) {
        synchronized (list){
            while(list.size()+num > MAX_SIZE){
                System.out.println("【要生產(chǎn)的產(chǎn)品數(shù)量】:" + num + "\t【庫(kù)存量】:" + list.size() + "\t暫時(shí)不能執(zhí)行生成任務(wù)!");
                try {
                    list.wait(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
 
            }
            for(int i=0;i<num;i++){
                list.add(new Object());
            }
 
            System.out.println("【已經(jīng)生產(chǎn)產(chǎn)品數(shù)】:" + num + "\t【現(xiàn)倉(cāng)儲(chǔ)量為】:" + list.size());
            list.notifyAll();
        }
    }
}

生產(chǎn)者類(lèi):

package threadtest.procon;
public class Producer extends Thread {
    private int num;
    public AbstractStorage abstractStorage;
 
    public Producer(AbstractStorage abstractStorage){
        this.abstractStorage = abstractStorage;
    }
 
    public void setNum(int num) {
        this.num = num;
    }
 
    public void produce(int num){
        abstractStorage.product(num);
    }
 
    @Override
    public void run() {
        produce(num);
    }
}

消費(fèi)者類(lèi):

package threadtest.procon;
public class Consumer extends Thread {
    private int num;
    public AbstractStorage abstractStorage;
 
    public Consumer(AbstractStorage abstractStorage){
        this.abstractStorage = abstractStorage;
    }
 
    public void setNum(int num){
        this.num = num;
    }
    public void consume(int num){
        this.abstractStorage.consume(num);
    }
 
    @Override
    public void run() {
        consume(num);
    }
}

測(cè)試類(lèi):

package threadtest.procon;
public class Test {
    public static void main(String[] args){
        AbstractStorage abstractStorage = new Storage();
 
        // 生產(chǎn)者對(duì)象
        Producer p1 = new Producer(abstractStorage);
        Producer p2 = new Producer(abstractStorage);
        Producer p3 = new Producer(abstractStorage);
        Producer p4 = new Producer(abstractStorage);
        Producer p5 = new Producer(abstractStorage);
        Producer p6 = new Producer(abstractStorage);
        Producer p7 = new Producer(abstractStorage);
 
        // 消費(fèi)者對(duì)象
        Consumer c1 = new Consumer(abstractStorage);
        Consumer c2 = new Consumer(abstractStorage);
        Consumer c3 = new Consumer(abstractStorage);
 
        // 設(shè)置生產(chǎn)者產(chǎn)品生產(chǎn)數(shù)量
        p1.setNum(10);
        p2.setNum(20);
        p3.setNum(30);
        p4.setNum(40);
        p5.setNum(30);
        p6.setNum(20);
        p7.setNum(80);
 
        // 設(shè)置消費(fèi)者產(chǎn)品消費(fèi)數(shù)量
        c1.setNum(50);
        c2.setNum(70);
        c3.setNum(20);
 
        c1.start();
        c2.start();
        c3.start();
 
        p1.start();
        p2.start();
        p3.start();
        p4.start();
        p5.start();
        p6.start();
        p7.start();
    }
}

以上就是詳解Java多線(xiàn)程與并發(fā)的詳細(xì)內(nèi)容,更多關(guān)于Java 多線(xiàn)程 并發(fā)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • java中transient關(guān)鍵字用法分析

    java中transient關(guān)鍵字用法分析

    這篇文章主要介紹了java中transient關(guān)鍵字用法,以實(shí)例形式分析了java中transient關(guān)鍵字的功能及使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • SpringBoot與velocity的結(jié)合的示例代碼

    SpringBoot與velocity的結(jié)合的示例代碼

    本篇文章主要介紹了SpringBoot與velocity的結(jié)合的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-03-03
  • 使用RestTemplate 調(diào)用遠(yuǎn)程接口上傳文件方式

    使用RestTemplate 調(diào)用遠(yuǎn)程接口上傳文件方式

    這篇文章主要介紹了使用RestTemplate 調(diào)用遠(yuǎn)程接口上傳文件方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 深入理解Java設(shè)計(jì)模式之單例模式

    深入理解Java設(shè)計(jì)模式之單例模式

    這篇文章主要介紹了JAVA設(shè)計(jì)模式之單例模式的的相關(guān)資料,文中示例代碼非常詳細(xì),供大家參考和學(xué)習(xí),感興趣的朋友可以了解下
    2021-11-11
  • 解決myBatis返回integer值的問(wèn)題

    解決myBatis返回integer值的問(wèn)題

    這篇文章主要介紹了解決myBatis返回integer值的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-11-11
  • Spring BeanPostProcessor接口使用詳解

    Spring BeanPostProcessor接口使用詳解

    本篇文章主要介紹了Spring BeanPostProcessor接口使用詳解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01
  • Java中字符數(shù)組和字符串與StringBuilder和字符串轉(zhuǎn)換的講解

    Java中字符數(shù)組和字符串與StringBuilder和字符串轉(zhuǎn)換的講解

    今天小編就為大家分享一篇關(guān)于Java中字符數(shù)組和字符串與StringBuilder和字符串轉(zhuǎn)換的講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-03-03
  • Java圖像之自定義角度旋轉(zhuǎn)(實(shí)例)

    Java圖像之自定義角度旋轉(zhuǎn)(實(shí)例)

    這篇文章主要介紹了Java圖像之自定義角度旋轉(zhuǎn)(實(shí)例),需要的朋友可以參考下
    2017-09-09
  • Struts2實(shí)現(xiàn)多文件上傳功能

    Struts2實(shí)現(xiàn)多文件上傳功能

    這篇文章主要為大家詳細(xì)介紹了Struts2實(shí)現(xiàn)多文件上傳功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • 解決mybatis中的mapper命名問(wèn)題

    解決mybatis中的mapper命名問(wèn)題

    這篇文章主要介紹了解決mybatis中的mapper命名問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06

最新評(píng)論