1. 程式人生 > >Java高併發——多執行緒基礎

Java高併發——多執行緒基礎

       在上一篇 JAVA高併發——瞭解並行世界 中我們回顧了一些多執行緒的概念知識。這裡先舉個例子,來看現實生活中的多執行緒例子:一個家庭中有爸爸、媽媽、兒子,家中有電視、洗衣機、書桌等,如果媽媽領著兒子出去了,爸爸就可以想幹什麼幹什麼(單執行緒);如果三人都在家,媽媽可以洗衣服、爸爸在書桌上工作、兒子看動畫片(三個執行緒使用不同的資源互不影響);如果兒子看動畫片,爸爸想看NBA視訊,就得等著兒子看完了(多執行緒公用資源,出現阻塞)……看見了吧,其實我們就是把程式編寫的如何像現實生活一樣,多個任務同時進行,有共享資源,有各自資源,但是大家能夠有條不紊的進行著……

      好,這篇我們來複習一下Java多執行緒的一些基礎知識,來看下這張思維導圖:

       一,首先需要了解執行緒的狀態有哪些,怎麼扭轉,其實也就是生命週期(現實生活做事也是可以這樣抽象)。我先看下Java,Thread類中對於state的定義:

public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

        好,看下這幾種狀態的相互轉化吧:

       二,接下來看下執行緒的基本操作:

       1,新建執行緒:這個不用說了,通過繼承Thread或者實現Runnable介面兩種方式。

       2,終止執行緒:一般來說,執行緒執行完畢就會結束,無須自動關閉。但是對於一些while true的執行緒如果想關閉呢?Thread類裡有stop()方法,但是已經不建議使用了。因為它是強制停止執行緒,無論執行緒處於什麼狀態,很容易出現執行緒正在處理一半資料被停止的情況,這樣非常容易造成資料不一致問題。所以慎用stop()(最好不用),通過下邊這種方式來停止哪些無限迴圈的執行緒:

public class StopThread extends Thread {

    volatile boolean stopme = false;

    //停止方法
    public void stopMe() {
        stopme = true;
    }

    @Override
    public void run() {
        while (true) {
            //死迴圈中如果出現停止標識,則直接跳出
            if (stopme) {
                System.out.println("exit by stop me");
                break;
            }

            System.out.println(System.currentTimeMillis());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

         3,執行緒中斷:是一種執行緒協作機制。是告訴目標執行緒一箇中斷通知,至於接到通知後如何處理,則有執行緒自己決定處理。

public class InterruptThread {


    /**
     * public void interrupt() 中斷執行緒
     * public static boolean interrupted() 判斷是否被中斷,並清除當前中斷標識
     * public boolean isInterrupted() 判斷是否被中斷
     */
    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread() {
            @Override
            public void run() {
                while (true) {
                    //判斷如果有中斷標識,則直接跳出
                    if (Thread.currentThread().isInterrupted()) {
                        System.out.println("exit by interrupted");
                        break;
                    }

                    System.out.println(System.currentTimeMillis());
                    Thread.yield();
                }
            }
        };

        t1.start();
        Thread.sleep(2000);
        //打上中斷標識
        t1.interrupt();
    }
}

      4,等待(wait)和通知(notify),也是為了支援執行緒之間的協作。方法都是在Object上定義的,例如執行緒A呼叫了obj.wiat()方法,那麼執行緒A就會停止執行,等到其它執行緒呼叫obj.notify()方法為止。

public class WaitNotifyThread {
    final static Object ob = new Object();

    public static class T1 extends Thread {
        @Override
        public void run() {
            synchronized (ob) {
                System.out.println(System.currentTimeMillis() + "t1 is start");

                try {
                    System.out.println(System.currentTimeMillis() + "t1 is wait");
                    ob.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(System.currentTimeMillis() + "t1 is end");

            }
        }
    }


    public static class T2 extends Thread {
        @Override
        public void run() {
            synchronized (ob) {
                System.out.println(System.currentTimeMillis() + "t2 is start");


                System.out.println(System.currentTimeMillis() + "t2 is notify start");
                ob.notify();
                System.out.println(System.currentTimeMillis() + "t2 is notify end");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(System.currentTimeMillis() + "t2 is end");

            }
        }
    }

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

        5,掛起(suspend)和繼續執行(resume):也是一對相對的操作,但是JDK也已經標示為廢棄方法,不推薦使用。應為使用suspend()去掛起導致執行緒暫停的同時,並不釋放資源,會阻塞其它執行緒,如果處理不當(例如resume()處理在suspend()之前了,就會導致一直阻塞),所以慎用。我們可以通過wait和notify來像實現stopMe()一樣,實現自己一個掛起和繼續執行操作。這裡不再演示。

        6,等待執行緒結束(join)和謙讓(yield):類似現實生活中,我做這件事得等著它做完那件事,我為他讓讓步等。很容易理解的:

public class JoinThread {
    volatile static int i = 0;

    public static  class AddThread extends Thread{
        @Override
        public void run() {
            for(i=0;i<10000;i++);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        AddThread addThread = new AddThread();
        addThread.start();
        //主執行緒等待addTread執行完畢
        addThread.join();
        System.out.println(i);
    }
}

       三,volatile:非常重要的關鍵字,如果用volatile修飾,虛擬機器就會特殊處理,不能隨意變動優化目標指令;並且修改後,各個執行緒都能看見它的改動,保證變數的可見性。在那個中共享變數是“一寫多讀”的情況下非常使用,保證修改及時可見。

       四,執行緒組:非常容易理解的概念,如果執行緒過多,為了方便管理,有了執行緒組,簡單看下使用:

public class ThreadGroupTest implements Runnable {
    public void run() {
        String groupName = Thread.currentThread().getThreadGroup().getName() + "--" + Thread.currentThread().getName();
        while (true){
            System.out.println(" i am " + groupName);
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        ThreadGroup threadGroup = new ThreadGroup("testGroup");
        Thread t1 = new Thread(threadGroup,new ThreadGroupTest(),"T1");
        Thread t2 = new Thread(threadGroup,new ThreadGroupTest(),"T2");
        t1.start();
        t2.start();
        System.out.println(threadGroup.activeCount());
        threadGroup.list();
    }
}

          五,守護執行緒(Daemon):是一種特殊的執行緒,聽名字猜想這種執行緒是系統的守護者。例如jvm的垃圾回收執行緒等。當一個Java應用中只有守護執行緒時,也就會自然退出:

public class DaemonDome {

    public static class T1 extends Thread {
        @Override
        public void run() {
            while (true) {
                System.out.println(" i am live");

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t = new T1();
        t.setDaemon(true);
        t.start();

        Thread.sleep(2000);
    }
}

       六,執行緒優先順序,這個比較好理解,優先順序從1到10數字越大優先順序越高,只是說概率更高而已,而並非一定。Tread提供三個靜態變數:

/**
     * The minimum priority that a thread can have.
     */
    public final static int MIN_PRIORITY = 1;

   /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;

       七,執行緒安全和synchronized:執行緒安全就是針對共享資源能夠高效有序的使用,不要出現同時修改,最終導致資料不一致的問題。而共享資源的同步使用即可解決。synchronized的作用就是實現執行緒間的同步。前邊已經用到,我們不在看例子,總結一下它的用法:1,指定加鎖物件:給物件加鎖,進入同步程式碼前要獲得給定物件的鎖:synchronized(obj);2,直接作用於例項方法:相當於對當前例項加鎖,進入同步程式碼前要獲得當前例項的鎖:synchronized(this);3,直接作用於靜態方法:相當於當前類加鎖,進入同步程式碼前要獲得當前類的鎖。

       好,高併發——Java多執行緒的複習總結就這樣,還是說結合實際生活來學習聯想。接下來,我們會繼續……