1. 程式人生 > >java多執行緒例項解析

java多執行緒例項解析

多執行緒含義:

1、首先從巨集觀上解釋一下多執行緒到底是個什麼東西,我們拿一個生活中例子來看一下;汽車上高速和出高速的時候都需要經過收費站吧,我們可以腦補下這個場景;現在汽車要下高速,收費站最主要的任務是對每輛車進行收費,這樣我們可以把收費站看成一個程式方法體,專門處理收費這個功能;這裡我們先要拋棄掉多個視窗的那種收費站,最原始的情況下,就像在一條上設了一個卡口,每輛車都要只能從這個一個卡口通過,這是原始的情況;當然我們知道這種方式效率很慢,所有的車都必須等待,只能從一個口出,進而為了提高效率收費站進行了改造,不是單一的視窗了,而是並排建了多個收費視窗,車輛可以選擇任意一個視窗繳費,因為每個視窗的功能都是一樣的;這樣一對比,比原始的單一視窗效率高出好幾倍;同樣程式程式碼中也是這樣,每個任務相當於每輛車,進入方法體都要執行繳費的功能,在程式程式碼中開闢多個執行緒來處理,提高執行效率;

2、好了,在大致理解什麼是多執行緒之後,我們在來看java對執行緒的解釋也就簡單的多了;java中對執行緒的解釋是這樣的,執行緒是程序的單位,程序是由執行緒組成;這裡我們可以把收費站看成一個單一功能的軟體,這個軟體開啟了一個收費程序;程序中最少也有一條執行緒在執行這裡什麼意思呢,就是說程序其實就是有執行緒組成的,確實,同樣拿收費站來看,最原始的時候收費站就只有一個視窗,就相當於一條執行緒,也可以是改良後的多個視窗,多個執行緒組成;

     同樣執行緒的一些特點也能解釋的同了,同一類執行緒共享資料空間和程式碼塊,程式碼塊就是指的收費站這個整體,而資料空間則是指每個視窗其實都是同一個資料庫,每條記錄都是記在同一張表中,不管是從哪個視窗錄入的;每個執行緒有獨立執行的棧和程式計數器,也就相當於每個視窗,每個收費視窗之間互不影響,每個視窗都記錄從該視窗繳費的車型,費用;

 實現多執行緒方法:

1)、繼承Thread類;

2)、實現Runnable介面;

3)、實現Callable介面,該介面是用於執行緒池中的,後面再講;

首先我們按順序先看Thread類:

/**
 * Created by zelei.fan on 2017/7/23.
 */
public class ThreadTest extends Thread {

    private int num;

    ThreadTest(int num){
        this.num = num;
    }

    @Autowired
    public void run(){
        System.out.println("繳費" + num);
    }
}
/**
 * Created by zelei.fan on 2017/7/23.
 */
public class test {

    public static void main(String[] args) {
        for (int i = 0; i < 10; i ++){
            int num = new Random().nextInt(100);/*取一個隨機數*/
            ThreadTest threadTest = new ThreadTest(num);
            threadTest.start();
        }
    }
}

實現runnable介面:

/**
 * Created by zelei.fan on 2017/7/23.
 */
public class ThreadTest implements Runnable {

    private int num;

    ThreadTest(int num){
        this.num = num;
    }

    @Autowired
    public void run(){
        System.out.println("繳費" + num);
    }
}
/**
 * Created by zelei.fan on 2017/7/23.
 */
public class test {

    public static void main(String[] args) {
        int num = new Random().nextInt(100);/*取一個隨機數*/
        ThreadTest threadTest = new ThreadTest(num);/*這邊例項其實是可以方法迴圈中,但是這楊就不能體現出和thread的差別了*/
        for (int i = 0; i < 10; i ++){
            new Thread(threadTest).start();
        }
    }
}

呼叫方式的區別:

繼承thread類的例項不能重複呼叫start(),原因是一個thread例項不能重複呼叫start(),因為當前這個thread已經變成可執行狀態,當再次呼叫start()方法時,有可能執行緒已經在執行過程中了,而執行緒中除了阻塞和可執行狀態之間可以相互逆轉之外,其他狀態時不能逆轉的,所以也就解釋了上述問題,執行中不能再次逆轉成可執行狀態;

但是實現runnable介面則不然,只new一個程式程式碼,但是多執行緒的時候是每用一個都是new一個新的thread類來啟動這個程式例項這樣就不存在上述thread的那樣的問題,同時也實現了多個執行緒處理同一資源;

兩者的區別:

1、runnable適合多個相同的程式程式碼的執行緒去處理同一資源;當用繼承thread類時,new出的多個ThreadTest例項,在同時啟動執行緒的時候這幾個執行緒都是在各自執行各自中的資源, 各自的執行緒中都有num這個資源,並且程式碼空間也都是互相獨立,因為ThreadTest被new出多個例項,這幾個執行緒並不共用同一例項;
但是實現runnable介面就不一樣了,new出一個RunnableTest例項後,下面幾個執行緒都是用的同一個例項來啟動執行緒,當執行到程式中時,每個執行緒用的程式碼都是同一個,而且用的資源num也是同一個,這樣就實現了多執行緒對同一資源的處理;

 2、可以避免java中的單繼承的限制,繼承thread:如果ThreadTest需要繼承一個父類程式碼,但是又同時想實現多執行緒這個功能,那就和java單繼承有衝突,實現runnable:可以實現在繼承thread的同時再實現runnable介面;

 3、程式碼可以被多個執行緒共享,程式碼和資料獨立; 程式碼共享:都是用的同一例項;程式碼和資料獨立:即程式碼和資料是分開的,資料是一個公共的資源,個執行緒之間都能使用

 4、執行緒池中只能放入runnable介面或者callable介面;

執行緒狀態轉換詳情:

1、建立一個執行緒(new Thread())
2、可執行狀態(呼叫start()方法)
3、正常情況下,獲取到cpu使用權,直接執行
4、阻塞:
1)、等待阻塞,執行緒呼叫wait()方法,jvm會吧該執行緒放到等待池中;
2)、同步阻塞,執行緒在獲取同步鎖時,同步鎖還在被其他執行緒佔用中,jvm把該執行緒放到鎖池中;
3)、其他阻塞,呼叫sleep()或者join(),執行緒sleep超時或者join等到其他執行緒終止,改線成則重新回到可執行狀態;(注意執行sleep時不釋放鎖)
5、執行緒結束

執行緒排程:

Thread.sleep(100);:sleep不會釋放物件鎖,而wait在呼叫後會釋放物件鎖
new Thread().wait();:執行緒等待,等到其他執行緒呼叫notify()或者notifyAll()方法來喚醒這些wait的執行緒
Thread.yield();:執行緒讓步,把資源讓給優先順序高的執行緒,讓當前執行緒回到可執行狀態,允許相同優先順序的其它執行緒獲得執行機會
new Thread().join();:等待其他執行緒終止,在當前執行緒中呼叫另一個執行緒的join方法,則當前執行緒進入阻塞狀態,直到另一個執行緒完成菜進入就緒狀態
new Thread().notify();:喚醒在等待的執行緒中的某一個執行緒,隨機喚醒其中一個;notifyAll()喚醒所有執行緒
Thread.interrupted();:終止執行緒,線上程阻塞的時候(join,sleep)執行會導致InterruptedException異常

執行緒排程具體例項:

jion():
不使用的時候:
/**
 * Created by zelei.fan on 2017/7/23.
 */
public class ThreadTest extends Thread {

    private String name;

    ThreadTest(String name){
        this.name = name;
    }

    ThreadTest(){}

    private int index = 5;

    @Override
    public void run(){
        System.out.println(Thread.currentThread().getName()+"執行緒開始執行");
        while (index > 0) {
            System.out.println(Thread.currentThread().getName()+name + "執行  :  " + index);
            index --;
        }
        System.out.println(Thread.currentThread().getName()+"執行緒執行結束");
    }

    public static void main(String[] args) {
        /*join用法
        * 作用:當主執行緒需要子執行緒的執行結果時,主執行緒必須等待子執行緒執行結束才能繼續執行
        * */
        System.out.println("主執行緒執行開始");
        ThreadTest a = new ThreadTest("A");
        a.start();
        System.out.println("主執行緒執行結束");
    }
}

列印結果:

主執行緒執行開始
主執行緒執行結束
Thread-0執行緒開始執行
Thread-0A執行  :  5
Thread-0A執行  :  4
Thread-0A執行  :  3
Thread-0A執行  :  2
Thread-0A執行  :  1
Thread-0執行緒執行結束

使用後:

/**
 * Created by zelei.fan on 2017/7/23.
 */
public class ThreadTest extends Thread {

    private String name;

    ThreadTest(String name){
        this.name = name;
    }

    ThreadTest(){}

    private int index = 5;

    @Override
    public void run(){
        System.out.println(Thread.currentThread().getName()+"執行緒開始執行");
        while (index > 0) {
            System.out.println(Thread.currentThread().getName()+name + "執行  :  " + index);
            index --;
        }
        System.out.println(Thread.currentThread().getName()+"執行緒執行結束");
    }

    public static void main(String[] args) {
        /*join用法
        * 作用:當主執行緒需要子執行緒的執行結果時,主執行緒必須等待子執行緒執行結束才能繼續執行
        * */
        System.out.println("主執行緒執行開始");
        ThreadTest a = new ThreadTest("A");
        a.start();
        try {
            a.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("主執行緒執行結束");
    }
}

列印結果:

主執行緒執行開始
Thread-0執行緒開始執行
Thread-0A執行  :  5
Thread-0A執行  :  4
Thread-0A執行  :  3
Thread-0A執行  :  2
Thread-0A執行  :  1
Thread-0執行緒執行結束
主執行緒執行結束

yield():執行緒讓步在用例子比較難體現出來,打印出來的結果幾乎一樣的,因為就算不執行執行緒讓步,每個執行緒都是互相搶佔cpu資源的,誰搶到誰執行,沒有固定的先後順序;

如下:

/**
 * Created by zelei.fan on 2017/7/23.
 */
public class ThreadTest extends Thread {

    private String name;

    ThreadTest(String name){
        this.name = name;
    }

    ThreadTest(){}

    private int index = 5;

    @Override
    public void run(){
        System.out.println(Thread.currentThread().getName()+"執行緒開始執行");
        while (index > 0) {
            System.out.println(Thread.currentThread().getName()+name + "執行  :  " + index);
            index --;
        }
        System.out.println(Thread.currentThread().getName()+"執行緒執行結束");
    }

    public static void main(String[] args) {
        
        ThreadTest a = new ThreadTest("A");
        ThreadTest b = new ThreadTest("B");
        a.start();
        b.start();
    }
}

列印結果:

Thread-0執行緒開始執行
Thread-1執行緒開始執行
Thread-1B執行  :  5
Thread-1B執行  :  4
Thread-0A執行  :  5
Thread-0A執行  :  4
Thread-0A執行  :  3
Thread-0A執行  :  2
Thread-0A執行  :  1
Thread-0執行緒執行結束
Thread-1B執行  :  3
Thread-1B執行  :  2
Thread-1B執行  :  1
Thread-1執行緒執行結束

使用後:這裡為了能清楚的看到這個執行過程,我在程式碼中設定只要是index為3是就讓步,不管是哪個執行緒

/**
 * Created by zelei.fan on 2017/7/23.
 */
public class ThreadTest extends Thread {

    private String name;

    ThreadTest(String name){
        this.name = name;
    }

    ThreadTest(){}

    private int index = 5;

    @Override
    public void run(){
        System.out.println(Thread.currentThread().getName()+"執行緒開始執行");
        while (index > 0) {
            if (3 == index){
                Thread.yield();
                System.out.println(Thread.currentThread().getName()+name + "執行緒讓步");
                index --;
            }else {
                System.out.println(Thread.currentThread().getName()+name + "執行  :  " + index);
                index --;
            }
        }
        System.out.println(Thread.currentThread().getName()+"執行緒執行結束");
    }

    public static void main(String[] args) {
       
        ThreadTest a = new ThreadTest("A");
        ThreadTest b = new ThreadTest("B");
        a.start();
        b.start();
    }
}

列印結果:

Thread-0執行緒開始執行
Thread-1執行緒開始執行
Thread-0A執行  :  5
Thread-1B執行  :  5
Thread-0A執行  :  4
Thread-0A執行緒讓步
Thread-1B執行  :  4
Thread-0A執行  :  2
Thread-0A執行  :  1
Thread-1B執行緒讓步
Thread-0執行緒執行結束
Thread-1B執行  :  2
Thread-1B執行  :  1
Thread-1執行緒執行結束
從上面的列印結果可以看出:當a讓步後,b獲得資源,然後執行;但是這只是一種情況,也有可能還是a繼續執行,他們之間是公平競爭,誰搶到誰執行;

wait()和notify():這兩個是搭檔,配合使用的;一個等待一個喚醒;

下面通過例項來講解下,需求是我要打印出ABABAB這種結果,不能亂:

class SortThread implements Runnable{

    private int index = 10;

    private String name;

    private Object prev;

    private Object self;

    SortThread(String name, Object prev, Object self){
        this.name = name;
        this.prev = prev;
        this.self = self;
    }

    @Override
    public void run() {
        while (index > 0) {
            synchronized (prev) {
                synchronized (self) {
                    System.out.print(name);
                    self.notify();
                    index--;
                }
                try {
                    prev.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

public static void main(String[] args) throws InterruptedException {
        Object a = new Object();
        Object b = new Object();
        SortThread sortThread = new SortThread("A", a, b);
        SortThread sortThread1 = new SortThread("B", b, a);
        new Thread(sortThread).start();
        Thread.sleep(100);
        new Thread(sortThread1).start();
    }

列印結果: ABABABABABABABABABAB

解釋:

先從main方法中的兩個物件說起,a和b其實分別是下面兩個執行緒的鎖,當然這邊涉及到鎖的知識後面再講,這兩個執行緒都持有自身的鎖和對方的鎖;Thread.sleep(100);主要是讓A能先執行;

然後我們再看run方法中做了什麼,第一步獲取對方的鎖和自身的鎖,當獲取到自身鎖時,就算B是可執行狀態也進不來,以為第一層B要執行就得先獲得A的鎖,而A的鎖已經被A在使用,只有等待A釋放;self.notify();喚醒其它執行緒,此時在門外等待的B執行緒被喚醒;執行完成後首先是釋放自身的鎖;當A釋放自身鎖的時候,B就能獲取到鎖進入第一層,然而B的自身鎖還沒有被A釋放;當A的自身鎖釋放後繼續執行到prev.wait();,此處執行就會立馬釋放B的鎖,只要一釋放B會立馬搶佔到自身鎖,然後執行方法,而此刻A執行緒則被關在門外;就是這樣迴圈下去;

相關推薦

java執行例項解析

多執行緒含義: 1、首先從巨集觀上解釋一下多執行緒到底是個什麼東西,我們拿一個生活中例子來看一下;汽車上高速和出高速的時候都需要經過收費站吧,我們可以腦補下這個場景;現在汽車要下高速,收費站最主要的任務是對每輛車進行收費,這樣我們可以把收費站看成一個程式方法體,專門處理收費

201711671125 Java執行例項(第十一週)

建立3個售票視窗同時開始銷售10張票。 原始碼: public class One {     public static void main(String[] args) {         Ti

Java執行詳細解析教程

1.原子性: 一個操作或者多個操作,要麼全部執行成功,要麼全部執行失敗。比如賬戶轉賬問題,A賬戶向B轉100,A賬戶減去100元,B賬戶加上一百元,這兩個操作必須具備原子性,才能保證資料的安全,所以需要鎖來保證資料的原子性。 2.可見性: 當一個執行緒修改變數之後,其他執行緒能夠立即看

Java執行程式設計詳細解析

Java多執行緒程式設計詳細解析   一、理解多執行緒多執行緒是這樣一種機制,它允許在程式中併發執行多個指令流,每個指令流都稱為一個執行緒,彼此間互相獨立。執行緒又稱為輕量級程序,它和程序一樣擁有獨立的執行控制,由作業系統負責排程,區別在於執行緒沒有獨立的儲存空間,而是和所屬程

java基礎總結(三十二)--java執行程式設計例項

來自:https://blog.csdn.net/qq_34996727/article/details/80416277或者https://www.cnblogs.com/pureEve/p/6524366.html 一.相關知識:   Java多執行緒程式設計到的知識:

JAVA執行wait與notify詳細解析(由生產者和消費者案例引出)

生產者和消費者這個關係是個經典的多執行緒案例。現在我們編寫一個Demo來模擬生產者和消費者之間的關係。 假如有兩個類,一個是資料生產者類DataProvider,另一個是資料消費者類DataConsumer,這兩個類同時對資料類Data進行操作,生產者類負責生產資料,消費者類負責消費資料,下面是

java執行(含例項)、並行、併發的含義

轉  https://www.cnblogs.com/wxd0108/p/5479442.html 這篇文章寫得非常棒, 我在這裡記錄一下,防止以後找不到了   用多執行緒只有一個目的,那就是更好的利用cpu的資源,因為所有的多執行緒程式碼都可以用單執行緒來

Java執行(二)Java併發工具包concurrent例項簡述

傳統的多執行緒並沒有提供高階特性,例如:訊號量、執行緒池和執行管理器等,而這些特性恰恰有助於建立強大的併發程式。新的Fork/Join框架針對當前的多核系統,也提供了並行程式設計的可能。這塊的內容是java多執行緒資訊量最大的一部分內容,本篇部落格循序漸進的,首

Java執行——FutureTask原始碼解析

一個很常見的多執行緒案例是,我們安排主執行緒作為分配任務和彙總的一方,然後將計算工作切分為多個子任務,安排多個執行緒去計算,最後所有的計算結果由主執行緒進行彙總。比如,歸併排序,字元頻率的統計等等。 我們知道Runnable是不返回計算結果的,如果想利用多執行緒的話,只能儲

java執行程式設計的核心——AQS原理解析

AQS是什麼 java concurrent包中有很多阻塞類如:ReentrantLock、ReentrantReadWriteLock、CountDownLatch、Semaphore、Synchronous、FutureTask等,他們的底層都是根據aqs構

Android/java執行(三)-HandlerThread的使用場景及原始碼解析

HandlerThread是什麼? 點開類楸一眼,這貨是這樣介紹自己的: * Handy class for starting a new thread that has a looper. The looper can then be * used to create h

Java執行同步---以銀行存取錢的過程的簡單程式碼例項

首先存錢取錢的這個操作,應該是執行緒操作的,可以有很多的顧客,這意思就是得有多個執行緒,多個執行緒之間共同操作一個銀行,銀行的金額就需要同步。才能保證執行緒安全。 所以,下面就把這個程式碼的例項放 這,有不對的地方,還請指出來哈。因為有個老鐵問這個多執行緒的程式碼。 首先是

java執行之callable+Executor執行例項

package main.java; import java.sql.Time; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.con

java 執行等待、喚醒機制例項

例子: 1、實體類 public class Student {     String name;     int age;     boolean flag = false; // 表示沒有值 } 2、執行緒1 public class SetThread impleme

Java 執行池ThreadPoolExecutor解析及Executors類中提供的靜態方法來建立執行

上面的程式碼可能看起來不是那麼容易理解,下面我們一句一句解釋:   首先,判斷提交的任務command是否為null,若是null,則丟擲空指標異常;   接著是這句,這句要好好理解一下: if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(c

java執行:結合執行交替列印10次abc例項,對wait/notify使用的徹底理解

有個朋友看點選開啟連結 裡的三個執行緒“A執行緒列印10次A,B執行緒列印10次B,C執行緒列印10次C,要求執行緒同時執行,交替列印10次ABC。” 這個問題的程式碼,有點不太理解,讓我給看下。不

Java執行應用例項

public class DefaultThreadPool<Job extends Runnable> implements ThreadPool<Job> { //最大執行緒數 private static final int MaxWorkerNumber=10; //預設

JAVA#執行初步例項--1-100輸出 札記

public static void main(String[] args) { SubThreadone st = new SubThreadone(); SubThreadtwo st2 = new SubThreadtwo(); st.star

java執行的共享變數訪問控制例項

最近打算去一家電商公司,對於高併發的資料訪問控制有著嚴格的要求,近期打算把多執行緒的知識在好好補一下。 執行緒排程有五個狀態; 開始,可執行,執行,阻塞,死亡。 啟動執行緒有兩種方法。繼承Thread類或則實現Runnable介面,其實Thread類也實現

Java執行:ThreadLocal解析

一、ThreadLocal是什麼 ThreadLocal是一個本地執行緒副本變數工具類。主要用於將私有執行緒和該執行緒存放的副本物件做一個對映,各個執行緒之間的變數互不干擾,在高併發場景下,可以實現無狀態的呼叫,特別適用於各個執行緒依賴不通的變數值完成操作的場景。 二、從