真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Java高并發(fā)回顧線程--多線程基礎(chǔ)詳細(xì)介紹

什么是線程:
線程是進程內(nèi)的執(zhí)行單元
某個進程當(dāng)中都有若干個線程。

超過10余年行業(yè)經(jīng)驗,技術(shù)領(lǐng)先,服務(wù)至上的經(jīng)營模式,全靠網(wǎng)絡(luò)和口碑獲得客戶,為自己降低成本,也就是為客戶降低成本。到目前業(yè)務(wù)范圍包括了:網(wǎng)站制作、網(wǎng)站設(shè)計,成都網(wǎng)站推廣,成都網(wǎng)站優(yōu)化,整體網(wǎng)絡(luò)托管,成都微信小程序,微信開發(fā),成都App制作,同時也可以讓客戶的網(wǎng)站和網(wǎng)絡(luò)營銷和我們一樣獲得訂單和生意!

線程是進程內(nèi)的執(zhí)行單元。

使用線程的原因是,進程的切換是非常重量級的操作,非常消耗資源。如果使用多進程,那么并發(fā)數(shù)相對來說不會很高。而線程是更細(xì)小的調(diào)度單元,更加輕量級,所以線程會較為廣泛的用于并發(fā)設(shè)計。

在Java當(dāng)中線程的概念和操作系統(tǒng)級別線程的概念是類似的。事實上,Jvm將會把Java中的線程映射到操作系統(tǒng)的線程區(qū)。Java 高并發(fā)  回顧線程--多線程基礎(chǔ)詳細(xì)介紹

Java 高并發(fā)  回顧線程--多線程基礎(chǔ)詳細(xì)介紹

當(dāng)new出一個線程時,其實線程并沒有工作。它只是生成了一個實體,當(dāng)你調(diào)用這個實例的start方法時,線程才真正地被啟動。啟動后到Runnable狀態(tài),Runnable表示該線程的資源等等已經(jīng)被準(zhǔn)備好,已經(jīng)可以執(zhí)行了,但是并不表示一定在執(zhí)行狀態(tài),由于時間片輪轉(zhuǎn),該線程也可能此時并沒有在執(zhí)行。對于我們來說,該線程可以認(rèn)為已經(jīng)被執(zhí)行了,但是是否真實執(zhí)行,還得看物理cpu的調(diào)度。當(dāng)線程任務(wù)執(zhí)行結(jié)束后,線程就到了Terminated狀態(tài)。

有時候在線程的執(zhí)行當(dāng)中,不可避免的會申請某些鎖或某個對象的監(jiān)視器,當(dāng)無法獲取時,這個線程會被阻塞住,會被掛起,到了Blocked狀態(tài)。如果這個線程調(diào)用了wait方法,它就處于一個Waiting狀態(tài)。進入Waiting狀態(tài)的線程會等待其他線程給它notify,通知到之后由Waiting狀態(tài)又切換到Runnable狀態(tài)繼續(xù)執(zhí)行。當(dāng)然等待狀態(tài)有兩種,一種是無限期等待,直到被notify。一直則是有限期等待,比如等待10秒還是沒有被notify,則自動切換到Runnable狀態(tài)。

代碼操作:
新建線程
Thread thread = new Thread();
thread.start();

這樣就開啟了一個線程。
有一點需要注意的是

Thread thread = new Thread();
thread.run();
直接調(diào)用run方法是無法開啟一個新線程的。
start方法其實是在一個新的操作系統(tǒng)線程上面去調(diào)用run方法。換句話說,直接調(diào)用run方法而不是調(diào)用start方法的話,它并不會開啟新的線程,而是在調(diào)用run的當(dāng)前的線程當(dāng)中執(zhí)行你的操作。

代碼實例:
Thread thread = new Thread("t1"){@Override
br/>@Override
{
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName());
}
};
thread.start();
如果調(diào)用start,則輸出是t1
Thread thread = new Thread("t1"){@Override
br/>@Override
{
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName());
}
};
thread.run();
如果是run,則輸出main。(直接調(diào)用run其實就是一個普通的函數(shù)調(diào)用而已,并沒有達到多線程的作用)

run方法的實現(xiàn)有兩種方式

第一種方式,直接覆蓋run方法,就如剛剛代碼中所示,最方便的用一個匿名類就可以實現(xiàn)。
Thread thread = new Thread("t1"){@Override
br/>@Override
{
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName());
}
};
第二種方式
Thread t1=new Thread(new CreateThread3());
CreateThread3()實現(xiàn)了Runnable接口。
在張孝祥的視頻中,推薦第二種方式,稱其更加面向?qū)ο蟆?/p>

終止線程
Thread.stop() 不推薦使用。它會釋放所有monitor (監(jiān)視器)
在源碼中已經(jīng)明確說明stop方法被Deprecated,在Javadoc中也說明了原因。

原因在于stop方法太過"暴力"了,無論線程執(zhí)行到哪里,它將會立即停止掉線程。
舉例說明:
Java 高并發(fā)  回顧線程--多線程基礎(chǔ)詳細(xì)介紹
當(dāng)寫線程得到鎖以后開始寫入數(shù)據(jù),寫完id = 1,在準(zhǔn)備將name = 1時被stop,釋放鎖。讀線程獲得鎖進行讀操作,讀到的id為1,而name還是0,導(dǎo)致了數(shù)據(jù)不一致。
最重要的是這種錯誤不會拋出異常,將很難被發(fā)現(xiàn)。

線程中斷
線程中斷有3種方法
public void Thread.interrupt() // 中斷線程
public boolean Thread.isInterrupted() // 判斷是否被中斷
public static boolean Thread.interrupted() // 判斷是否被中斷,并清除當(dāng)前中斷狀態(tài)
什么是線程中斷呢?
如果不了解Java的中斷機制,這樣的一種解釋極容易造成誤解,認(rèn)為調(diào)用了線程的interrupt方法就一定會中斷線程。
其實,Java的中斷是一種協(xié)作機制。也就是說調(diào)用線程對象的interrupt方法并不一定就中斷了正在運行的線程,它只是要求線程自己在合適的時機中斷自己。每個線程都有一個boolean的中斷狀態(tài)(不一定就是對象的屬性,事實上,該狀態(tài)也確實不是Thread的字段),interrupt方法僅僅只是將該狀態(tài)置為true。對于非阻塞中的線程, 只是改變了中斷狀態(tài), 即Thread.isInterrupted()將返回true,并不會使程序停止;
舉例:
public void run(){//線程t1
while(true){
Thread.yield();
}
}
t1.interrupt();
這樣使線程t1中斷,是不會有效果的,只是更改了中斷狀態(tài)位。
如果希望非常優(yōu)雅地終止這個線程,就該這樣做
public void run(){
while(true)
{
if(Thread.currentThread().isInterrupted()){
System.out.println("Interruted!");
break;
}
Thread.yield();
}
}
使用中斷,就對數(shù)據(jù)一致性有了一定的保證。

對于可取消的阻塞狀態(tài)中的線程, 比如等待在這些函數(shù)上的線程, Thread.sleep(), Object.wait(), Thread.join(), 這個線程收到中斷信號后, 會拋出InterruptedException, 同時會把中斷狀態(tài)置回為false.

對于取消阻塞狀態(tài)中的線程,可以這樣抒寫代碼:

public void run(){
while(true){
if(Thread.currentThread().isInterrupted()){
System.out.println("Interruted!");
break;
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println("Interruted When Sleep");
//設(shè)置中斷狀態(tài),拋出異常后會清除中斷標(biāo)記位
Thread.currentThread().interrupt();
}
Thread.yield();
}
}

線程掛起
掛起(suspend)和繼續(xù)執(zhí)行(resume)線程

suspend()不會釋放鎖
如果加鎖發(fā)生在resume()之前 ,則死鎖發(fā)生
這兩個方法都是Deprecated方法,不推薦使用。

原因在于,suspend不釋放鎖,因此沒有線程可以訪問被它鎖住的臨界區(qū)資源,直到被其他線程resume。因為無法控制線程運行的先后順序,如果其他線程的resume方法先被運行,那則后運行的suspend,將一直占有這把鎖,造成死鎖發(fā)生。

舉例:
package test;

public class Test{
static Object u = new Object();
static TestSuspendThread t1 = new TestSuspendThread("t1");
static TestSuspendThread t2 = new TestSuspendThread("t2");

public static class TestSuspendThread extends Thread{
public TestSuspendThread(String name){
setName(name);
}

@Override
public void run(){
synchronized (u)
{
System.out.println("in " + getName());
Thread.currentThread().suspend();
}
}
}

public static void main(String[] args) throws InterruptedException {
t1.start();
Thread.sleep(100);
t2.start();
t1.resume();
t2.resume();
t1.join();
t2.join();
}
}
讓t1,t2同時爭奪一把鎖,爭奪到的線程suspend,然后再resume,按理來說,應(yīng)該某個線程爭奪后被resume釋放了鎖,然后另一個線程爭奪掉鎖,再被resume。
結(jié)果輸出是:

in t1
in t2

說明兩個線程都爭奪到了鎖,但是控制臺的紅燈還是亮著的,說明t1,t2一定有線程沒有執(zhí)行完。我們dump出堆來看看
打開cmd
輸入jps會看到
Java 高并發(fā)  回顧線程--多線程基礎(chǔ)詳細(xì)介紹
將進程號復(fù)制
輸入jstack 進程號 然后就可以看到
Java 高并發(fā)  回顧線程--多線程基礎(chǔ)詳細(xì)介紹
發(fā)現(xiàn)t2一直被suspend。這樣就造成了死鎖。

join和yeild
yeild是個native靜態(tài)方法,這個方法是想把自己占有的cpu時間釋放掉,然后和其他線程一起競爭(注意yeild的線程還是有可能爭奪到cpu,注意與sleep區(qū)別)。在javadoc中也說明了,yeild是個基本不會用到的方法,一般在debug和test中使用。

join方法的意思是等待其他線程結(jié)束,就如suspend那節(jié)的代碼,想讓主線程等待t1,t2結(jié)束以后再結(jié)束。沒有結(jié)束的話,主線程就一直阻塞在那里。

舉例:
package test;

public class Test{
public volatile static int i = 0;

public static class AddThread extends Thread{@Override
br/>@Override
for (i = 0; i < 10000000; i++)
;
}
}

public static void main(String[] args) throws InterruptedException{
AddThread at = new AddThread();
at.start();
at.join();
System.out.println(i);
}
}
如果把上述代碼的at.join去掉,則主線程會直接運行結(jié)束,i的值會很小。如果有join,打印出的i的值一定是10000000。
那么join是怎么實現(xiàn)的呢?

join的本質(zhì)

while(isAlive()){
wait(0);
}
join()方法也可以傳遞一個時間,意為有限期地等待,超過了這個時間就自動喚醒。
這樣就有一個問題,誰來notify這個線程呢,在thread類中沒有地方調(diào)用了notify?

在javadoc中,找到了相關(guān)解釋。當(dāng)一個線程運行完成終止后,將會調(diào)用notifyAll方法去喚醒等待在當(dāng)前線程實例上的所有線程,這個操作是jvm自己完成的。

所以javadoc中還給了我們一個建議,不要使用wait和notify/notifyall在線程實例上。因為jvm會自己調(diào)用,有可能與你調(diào)用期望的結(jié)果不同。

守護線程
在后臺默默地完成一些系統(tǒng)性的服務(wù),比如垃圾回收線程、JIT線程就可以理解為守護線程。
當(dāng)一個Java應(yīng)用內(nèi),所有非守護進程都結(jié)束時,Java虛擬機就會自然退出。
此前有寫過一篇python中如何實現(xiàn),查看這里。

而Java中變成守護進程就相對簡單了。

Thread t=new DaemonThread();
t.setDaemon(true);
t.start();
這樣就開啟了一個守護線程。
package test;

public class Test{
public static class DaemonThread extends Thread{@Override
br/>@Override
for (int i = 0; i < 10000000; i++){
System.out.println("hi");
}
}
}

public static void main(String[] args) throws InterruptedException{
DaemonThread dt = new DaemonThread();
dt.start();
}
}
當(dāng)線程dt不是一個守護線程時,在運行后,我們能看到控制臺輸出hi
當(dāng)在start之前加入

dt.setDaemon(true);
控制臺就直接退出了,并沒有輸出。

線程優(yōu)先級
public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;
package test;

public class Test{
public static class High extends Thread{
static int count = 0;@Override
br/>@Override
while (true){
synchronized (Test.class){
count++;
if (count > 10000000){
System.out.println("High");
break;
}
}
}
}
}
public static class Low extends Thread{
static int count = 0;@Override
br/>@Override
while (true){
synchronized (Test.class){
count++;
if (count > 10000000){
System.out.println("Low");
break;
}
}
}
}
}

public static void main(String[] args) throws InterruptedException{
High high = new High();
Low low = new Low();
high.setPriority(Thread.MAX_PRIORITY);
low.setPriority(Thread.MIN_PRIORITY);
low.start();
high.start();
}
}
Thread類中有3個變量定義了線程優(yōu)先級。
讓一個高優(yōu)先級的線程和低優(yōu)先級的線程同時爭奪一個鎖,看看哪個最先完成。
當(dāng)然并不一定是高優(yōu)先級一定先完成。再多次運行后發(fā)現(xiàn),高優(yōu)先級完成的概率比較大,但是低優(yōu)先級還是有可能先完成的。

基本的線程同步操作
synchronized:
Object.wait() Object.notify()
指定加鎖對象:對給定對象加鎖,進入同步代碼前要獲得給定對象的鎖。
直接作用于實例方法:相當(dāng)于對當(dāng)前實例加鎖,進入同步代碼前要獲得當(dāng)前實例的鎖。
直接作用于靜態(tài)方法:相當(dāng)于對當(dāng)前類加鎖,進入同步代碼前要獲得當(dāng)前類的鎖。
作用于實例方法,則不要new兩個不同的實例
作用于靜態(tài)方法,只要類一樣就可以了,因為加的鎖是類.class,可以new兩個不同實例。

舉例:
指定加鎖對象
public class AS implements Runnable{
static AS instance = new AS();
static int i =0;@Override
br/>@Override
for(int j=0;j<10000000;j++){
synchronized(instance){
i++:
}
}
}

public static void main(String[] args) throws InterruptedException{
    Thread t1 = new Thread(instance);
    Thread t2 = new Thread(instance);
    t1.start();t2.start();
    t1.join();t2.join();
    System.out.println(i);
}

}
作用于實例方法
public class AS2 implements Runnable{
static AS2 instance = new AS2();
static int i = 0;
public synchronized void increase(){i++;
}
@Override
br/>i++;
}
@Override
for(int j=0;j<1000000;j++){
increase();
}
}
public static void main(String[] args) throws InterruptedException{
Thread t1 = new Thread(instance);
Thread t2 = new Thread(increase);
t1.start();t2.start();
t1.join();t2.join();
System.out.println(i);
}
}
作用于靜態(tài)方法
public class ASClass implements Runnable{
static int i=0;
public static synchronized increase(){i++;
}
@Override
br/>i++;
}
@Override
for(int j=0;j<1000000;i++){
increase();
}
}
public static void main(String[] args) throws InterruptedException{
Thread t1 = new Thread(new ASClass());
Thread t2 = new Thread(new ASClass());
t1.start();t2.start();
t1.join();t2.join();
System.out.println(i);
}
}

wait和notify的用法:
public static class T1 extends Thread{
public void run(){
synchronized(object){
System.out.println(System.currentTimeMillis()+":T1 start");

        try{
            System.out.println(System.currentTimeMillis()+":T1 wait for object");
            object.wait();
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        System.out.println(System.currentTimeMillis()+":T1 the end");
    }
}

}
public static class T2 extends Thread{
public void run(){
synchronized(object){
System.out.println(System.currentTimeMillis()+":T2 start! notify one thread");
object.notify();
System.out.println(System.currentTimeMillis()+":T2 the end");
try{
Thread.sleep(2000);
}catch(InterruptedException e){
e.printStackTrace();
}

    }
}

}
public static void main(String[] args){
Thread t1 = new T1();
Thread t2 = new T2();
t1.start();
t2.start();
}
PS:
synchronized(object){
object.notify();
}
必須拿到監(jiān)視器才能往下走,所以如果沒有拿到監(jiān)視器會報錯。
Java 高并發(fā)  回顧線程--多線程基礎(chǔ)詳細(xì)介紹

notifyAll:
public static class T1 extends Thread{
public void run(){
synchronized(object){
System.out.println(System.currentTimeMillis()+":T1 start! wait on object");

        try{
            object.wait();
            Thread.sleep(1000);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        System.out.println(System.currentTimeMillis()+":T1 the end");
    }
}

}
public static class T2 extends Thread{
public void run(){
synchronized(object){
System.out.println(System.currentTimeMillis()+":T2 start! notify all thread");
object.notifyAll();
System.out.println(System.currentTimeMillis()+":T2 the end");
try{
Thread.sleep(2000);
}catch(InterruptedException e){
e.printStackTrace();
}

    }
}

}
public static void main(String[] args){
Thread t1 = new T1();
Thread t12 = new T1();
t1.start();
t12.start();
Thread.sleep(1000);
Thread t2 = new T2();
t2.start();
}


本文標(biāo)題:Java高并發(fā)回顧線程--多線程基礎(chǔ)詳細(xì)介紹
分享地址:http://weahome.cn/article/pshcdd.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部