天天看點

Java高并發(二)——多線程基礎

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

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

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;
    }
           

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

Java高并發(二)——多線程基礎

       二,接下來看下線程的基本操作:

       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多線程的複習總結就這樣,還是說結合實際生活來學習聯想。接下來,我們會繼續……