1. 程式人生 > >Java基礎--併發程式設計

Java基礎--併發程式設計

JAVA併發程式設計第一步:瞭解Java虛擬機器提供的API操作,以及執行緒基本概念的操作:

1:定義執行緒 -- 繼承Thread類和實現Runnable方法

 1 /**
 2      * 定義執行緒 1:繼承Thread類,方法、形式如下
 3      */
 4     public static class T1 extends Thread{
 5         @Override
 6         public void run() {
 7             System.out.println("Threads.T1.run()");
 8         }
9 10 public static void main(String[] args) { 11 /** 12 * 啟動執行緒:只需要使用new關鍵字建立一個執行緒物件,並且將它 start()起來即可。 13 */ 14 T1 t1 = new T1(); 15 Thread thread = new Thread(t1); 16 thread.start(); 17 T2 t2 = new T2(); 18
Thread thread2 = new Thread(t2); 19 thread2.start(); 20 } 21 } 22 /** 23 *定義執行緒 實現runnable介面,方法、形式如下 24 */ 25 public static class T2 implements Runnable{ 26 27 public void run() { 28 System.out.println("Threads.t2.run()"); 29 }
30 31 }

2:執行緒終止--  不要用 API 提供的 stop() , stop太暴力,應自己寫邏輯實現

 

public static class StopThread implements Runnable{
        volatile boolean stopme = false;
        public void stopeMe(){
            stopme = true;
        }
        public void run() {
            while (true) {
                if (stopme) {
                    System.out.println("exit by stop me");
                    break;
                }
            }
        }
    }

3:執行緒中斷

    /**
     *  執行緒中斷
     *  public void Thread.interrupt()   //中斷執行緒
     *  Public void boolean Thread.isInterrupted()   //判斷執行緒是否被中斷
     *  Public static boolean Thread.interrupted()   //判斷是否被中斷,並請除當前中斷狀態
     */
    public static class InterrupThread implements Runnable{

        public void run() {
            while(true){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                System.out.println("Thread running");
            }
        }

        public static void main(String[] args) {
            /**
             * 啟動執行緒:只需要使用new關鍵字建立一個執行緒物件,並且將它start()起來即可。
             */
            InterrupThread interrupThread = new InterrupThread();
            Thread thread =  new Thread(interrupThread);
            thread.start();
//            Thread.sleep(1000);
            /**
             * thread 會 產生中斷,但是並沒有處理中斷,遇到 sleep 丟擲異常
             * 當程式出現 wait 和sleep 的時候,中斷會被忽略掉
             * 如果執行緒執行到了sleep()程式碼段,主程式中斷執行緒,執行緒這這時候丟擲異常,
             * 進入catch的異常處理。在catch程式碼段中,由於捕獲到了中斷,我們可以立即退出執行緒。
             * 在這裡我們並沒有這麼做,因為也許在這段程式碼中,我們還必須進行後續處理,保證資料的一致性和完整性,
             * 因此,執行了Thread.interrupt()方法在次中斷自己,設定中斷標誌位。
             */
            thread.interrupt();
            System.out.println(thread.isInterrupted());
        }
    }

4:等待和通知

public class WaitAndNotify {
    /**
     * JDK提供了兩個非常重要的介面,執行緒等待wait()方法和執行緒通知方法notify()。這兩個方法不是在Thread類中的
     * object.notifyAll()方法,他會喚醒等待佇列中的所有執行緒。
     */
    final static Object object = new Object();
    public static class MyThread_1 extends Thread{
        @Override
        public void run(){
            synchronized (object) {
                System.out.println(System.currentTimeMillis()+"T1 start");
                try {
                    System.out.println(System.currentTimeMillis()+"T1 wait");
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(System.currentTimeMillis()+"T1 end");
            }
        }
    }
    public static class MyThread_2 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                System.out.println(System.currentTimeMillis() + "T2 start and notify");
                object.notify();
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        Thread t1 = new MyThread_1();
        Thread t2 = new MyThread_2();
        t1.start();
        t2.start();

    }
}

5:volatile關鍵字解析

public class VolatileApp {
    /**
     * 這裡的 ready  和 number 相當於 被 volatile 修飾的變數
     * volatile來宣告一個變數時,就等於告訴了虛擬機器,這個變數極有可能會被某些程式或者是執行緒修改。
     * 為了確保這個變數被修改後,應用程式範圍內所有執行緒都能看到這個改動,虛擬機器就必須採取一些特殊的手段,保證這個變數的可見性等特點。
     */
    private static boolean ready;
    private static int number;
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread =   new MyThread();
        Thread thread = new Thread(myThread);
        thread.start();
        thread.sleep(1000);
        ready = false;
        number = 100;
        thread.sleep(2000);
    }
    public static class MyThread implements Runnable{

        public void run() {
            while(!ready)
                System.out.println(number);
        }
    }
}

6:執行緒組概念

public class ThreadGroupApp {
    /**
     * 構造方法:
     *
     * ThreadGroup(String name):以指定執行緒組名字來建立新執行緒組
     *
     * ThreadGroup(ThreadGroup parent,String name):以指定的名字、指定的父執行緒組來建立一個新執行緒組。
     *
     * 常用操作方法:
     *
     * · int activeCount():獲取執行緒組中活動執行緒的數量
     *
     * · interrupt():中斷執行緒組中所有執行緒
     *
     * · isDaemon():是否為後臺執行緒組
     *
     * · setDaemon(boolean daemon):設定為後臺執行緒組
     *
     * · setMaxPriority(int pri):設定執行緒組的最高優先順序
     *
     *   執行緒都是在創造的同時加入執行緒組中,然後才start。上述程式碼展示了執行緒組的兩個重要功能,
     *
     *   activeCount()可以獲得活動執行緒的總數,但由於執行緒是動態的,所以這個值是一個預估值,
     *
     *   list()可以列印這個執行緒組中所有的執行緒資訊,對除錯有一定幫助。
     */
    public static void main(String[] args) {
        ThreadGroup threadGroup = new ThreadGroup("ThreadGroupApp");
        MyThread myThread_1 = new MyThread(threadGroup,"myThread_1");
        MyThread myThread_2 = new MyThread(threadGroup,"myThread_2");
        myThread_1.setDaemon(true);//設定守護執行緒 ,必須在start 之前執行
        myThread_1.setPriority(1);//設定執行緒優先順序  1-10
        myThread_1.start();
        myThread_2.start();
        System.out.println(threadGroup.activeCount());
        threadGroup.list();
    }
    public static class MyThread extends Thread{
        public MyThread(String name){
            super(name);
        }
        public MyThread(ThreadGroup group,String name){
            super(group, name);
        }
        @Override
        public void run() {
            super.run();
        }
    }
}

7:synchronized 執行緒安全概念

/**
 * 執行緒安全 synchronized
 * 除了執行緒同步,確保執行緒安全, synchronized 確保執行緒之間可見、有序
 * 從可見性的角度上講,synchronized完全可以代替volatile的功能,只是使用上沒有那麼方便。就有序性而言,
 * 由於synchronized每一次只有一個執行緒可以訪問同步塊,因此,無論同步塊內的程式碼如何被亂序執行,只要保證序列語義一致,那麼執行的結果總是一樣的。
 * 換而言之,被synchronized限制的多個執行緒是序列執行的。
 */
public class ThreadSafeApp {
    public static int number;

    public static void main(String[] args) throws InterruptedException {
        AddThread addThread_1 = new AddThread();
        AddThread addThread_2 = new AddThread();
        Thread thread_1 = new Thread(addThread_1);
        Thread thread_2 = new Thread(addThread_2);
        thread_1.start();
        thread_2.start();
        thread_1.join();
        thread_2.join(); // 等待執行緒結束
        System.out.println(number);
    }
    public static class AddThread implements  Runnable{
        /**
         * 牽扯到記憶體模型,每一個類 new 的時候會生成方法區,在堆中獨立的空間所以宣告synchronized 同步方法只是在這個類中構造獨立,那麼兩個記憶體空間還是相互影響
         * 資料不一致 -- 解決辦法為:設定成靜態
         */
        public static synchronized  void add(){
            number++;
        }
        public void run() {
//            synchronized (AddThread.class) {
                for (int i = 0; i < 10000; i++) {
//                    number++;
                    add();
                }
            }
//        }
    }
}

8:重入鎖

public class LockApp {
    public static ReentrantLock Lock = new ReentrantLock();
    public static int Count = 0;

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

    }
    public static class ThreadLock extends Thread{
        @Override
        public void run() {
            for(int i=0;i<10000;i++) {
                /**
                 * 必須手動指定何時加鎖 ,何時釋放鎖。也正是因為這樣,
                 * 重入鎖邏輯控制遠遠要好於synchronized。
                 * 但值得注意的是,在退出零界區時,必須記得要釋放鎖,否者永遠沒有機會再訪問零界區了,會造成其執行緒的飢餓甚至是死鎖。
                 * 針對 lock() 可以多次呼叫! 但是必須釋放
                 */
                Lock.lock();
                Count++;
                Lock.unlock();
            }
        }
    }
}

9:lockInterruptibly 重入鎖的中斷

/**
 * 重入鎖的中斷 lockInterruptibly
 * 優先考慮響應中斷,而不是響應鎖的普通獲取或重入獲取。
 */
public class ThreadInterruptibilyApp {
    public static ReentrantLock Lock1 = new ReentrantLock();
    public static ReentrantLock Lock2 = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        MyThread t1 = new MyThread(1);
        MyThread t2 = new MyThread(2);
        t1.start();
        t2.start();
        Thread.sleep(3000);
        t2.interrupt();
        /**
         *   在t1和t2執行緒start後,主執行緒main進入休眠,此時t1和t2執行緒處於死鎖狀態,
         *   然後主執行緒main中斷t2執行緒,
         *   故t2會放棄對lock1的請求,同時釋放lock2。這個操作使得t1可以獲得lock2從而繼續執行下去。
         *   那麼,完成工作的只有t1 ,t2 是直接放棄推出,釋放資源
         */
    }

    public static class MyThread extends Thread{
        int flag;
        MyThread(int flag) {
            this.flag = flag;

        }

        @Override
        public void run() {
            try{
                if(flag == 1){
                    try {
                        Lock1.lockInterruptibly();
                        Thread.sleep(1000);
                        Lock2.lockInterruptibly();
                        System.out.println(flag+"號執行緒:完成工作");
                    } catch (InterruptedException e) {}
                }
                else if(flag == 2){
                    try {
                        Lock2.lockInterruptibly();
                        Thread.sleep(1000);
                        Lock1.lockInterruptibly();
                        System.out.println(flag+"號執行緒:完成工作");
                    } catch (InterruptedException e) {}
                }
            }finally{
                //中斷響應
                if(Lock1.isHeldByCurrentThread()){
                    Lock1.unlock();
                    System.out.println(flag+":Lock1 interrupted unlock");
                }
                if(Lock2.isHeldByCurrentThread()){
                    Lock2.unlock();
                    System.out.println(flag+":Lock2 interrupted unlock");
                }
                System.out.println(flag+"號執行緒退出");
            }
        }
    }
}

10:Map、ArrayList、Vector、ConcurrenHashMap 執行緒安全測試

public class ThreadErrorApp {
    public static Map<String,String> threadMap = new HashMap<String, String>();
    public static ArrayList<Integer> threadArrayList = new ArrayList<Integer>();
    public static Vector<Integer> threadVector = new Vector<Integer>();
    public static void main(String[] args) throws InterruptedException {
//        MyThread myThread_1 = new MyThread();
//        MyThread myThread_2 = new MyThread();
//        Thread thread_1 = new Thread(myThread_1);
//        Thread thread_2 = new Thread(myThread_2);
//        thread_1.start();
//        thread_2.start();
//        thread_1.join();
//        thread_2.join();
//        System.out.println(threadVector.size());
        MyThreadMap myThread_1 = new MyThreadMap();
        MyThreadMap myThread_2 = new MyThreadMap();
        Thread thread_1 = new Thread(myThread_1);
        Thread thread_2 = new Thread(myThread_2);
        thread_1.start();
        thread_2.start();
        thread_1.join();
        thread_2.join();
        System.out.println(threadMap.size());
    }

    /**
     * 1: 報錯java.lang.ArrayIndexOutOfBoundsException 因為記憶體一致性 遭到破壞,產生越界問題
     * 2:size 小於 20000
     * 解決辦法 使用Vector
     */
    public static class MyThread extends Thread {
        @Override
        public void run() {
            for (int i=0;i<10000;i++){
//                threadArrayList.add(i);
                threadVector.add(i);
            }
        }
    }

    /**
     * 程式正常結束,但結果不符合預期,而是一個小於100000的數字。
     * 使用 ConcurrenHashMap 代替
     */
    public  static class MyThreadMap extends Thread{
        @Override
        public void run() {
            for (int i=0;i<10000;i++){
                threadMap.put(Integer.toString(i),Integer.toString(i));
            }
        }
    }
}