Java高並發編程(三),java並發編程

來源:互聯網
上載者:User

Java高並發編程(三),java並發編程
 目錄:

  1.安全執行緒單例模式的幾種實現方式

  2.同步容器

  3.並發容器

  一、安全執行緒單例模式的幾種實現方式

  1.餓漢式(不使用同步鎖,典型的用空間換時間)

public class Singleton1 {     private static Singleton1 mySingleton = new Singleton1();    private Singleton1(){         System.out.println("single");    }         public static Singleton1 getSingle(){        return mySingleton;    }    public static void main(String[] args) {        Thread[] ths = new Thread[200];        for(int i=0; i<ths.length; i++) {            ths[i] = new Thread(()->{                Singleton1.getSingle();            });        }                Arrays.asList(ths).forEach(o->o.start());    }} 

運行結果:

  2.懶漢式(使用同步鎖,延時載入,典型的時間換空間)

public class Singleton2 {          private static Singleton2 mySingleton;          private Singleton2 (){           System.out.println("single");     }             public static synchronized Singleton2 getSingle(){    //對擷取執行個體的方法進行同步        if (mySingleton == null)                 mySingleton = new Singleton2();         return mySingleton;    }        public static void main(String[] args) {        Thread[] ths = new Thread[200];        for(int i=0; i<ths.length; i++) {            ths[i] = new Thread(()->{                Singleton2.getSingle();            });        }                    Arrays.asList(ths).forEach(o->o.start());    }}  

運行結果:

  3.雙重同步鎖(縮小粒度,雙重檢查

public class Singleton3 {           private volatile static Singleton3 mySingleton;           private Singleton3 (){        System.out.println("single");    }            public static Singleton3 getSingle(){    //對擷取執行個體的方法進行同步       if (mySingleton == null){           synchronized(Singleton3.class){               if (mySingleton == null)                   mySingleton = new Singleton3();            }       }       return mySingleton;    }        public static void main(String[] args) {        Thread[] ths = new Thread[200];        for(int i=0; i<ths.length; i++) {            ths[i] = new Thread(()->{                Singleton3.getSingle();            });        }                    Arrays.asList(ths).forEach(o->o.start());    }}

運行結果:

  為mySingleton加上volatile關鍵字,以確保能先行發生關係(happens-before relationship),使所有的寫操作都發生於讀操作之前,避免出現在另一個線程中看到一個初始化一半的mySingleton的情況,同時雙重同步鎖的效率也很高

  4.使用內部類的單例模式(既不用加鎖,也能實現懶載入)

public class Singleton4 {        private Singleton4() {        System.out.println("single");    }        private static class Inner {        private static Singleton4 mySingleton = new Singleton4();    }        public static Singleton4 getSingle() {        return Inner.mySingleton;    }        public static void main(String[] args) {        Thread[] ths = new Thread[200];        for(int i=0; i<ths.length; i++) {            ths[i] = new Thread(()->{                Singleton4.getSingle();            });        }                Arrays.asList(ths).forEach(o->o.start());    }    }

運行結果:

  二、同步容器

  1.vector、stack、hashtable等,保證了方法的原子性

  2.小例子程式示範同步容器

/** * 有N張火車票,每張票都有一個編號 * 同時有10個視窗對外售票 * 請寫一個類比程式 *  * 分析下面的程式可能會產生哪些問題? * 重複銷售?超量銷售? *  */package yxxy.c_024;import java.util.ArrayList;import java.util.List;public class TicketSeller1 {    static List<String> tickets = new ArrayList<>();        static {        for(int i=0; i<10000; i++) tickets.add("票編號:" + i);    }                public static void main(String[] args) {        for(int i=0; i<10; i++) {            new Thread(()->{                while(tickets.size() > 0) {                    System.out.println("銷售了--" + tickets.remove(0));                }            }).start();        }    }}

運行結果:

出現了越界情況,說明發生了超量銷售與重複銷售的情況

下面使用同步容器,以vector為例再做一遍售票操作

/** * 有N張火車票,每張票都有一個編號 * 同時有10個視窗對外售票 * 請寫一個類比程式 *  * 分析下面的程式可能會產生哪些問題? *   * 使用Vector或者Collections.synchronizedXXX * 分析一下,這樣能解決問題嗎? *  */package yxxy.c_024;import java.util.Vector;import java.util.concurrent.TimeUnit;public class TicketSeller2 {    static Vector<String> tickets = new Vector<>();            static {        for(int i=0; i<1000; i++) tickets.add("票 編號:" + i);    }        public static void main(String[] args) {                for(int i=0; i<10; i++) {            new Thread(()->{                while(tickets.size() > 0) {                                    /*    try {                        TimeUnit.MILLISECONDS.sleep(10);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }*/                                                            System.out.println("銷售了--" + tickets.remove(0));                }            }).start();        }    }}

運行結果:

...

從運行結果來看,同步容器似乎已經達到了我們想要的目的,但其中還有一些問題

當我們使用了同步容器時,雖然容器的方法是具有原子性的,但當判斷與操作分離時,中間的部分還是可能會被其他線程所打斷,下面我們類比一下判斷與remove之間有其他的邏輯

/** * 有N張火車票,每張票都有一個編號 * 同時有10個視窗對外售票 * 請寫一個類比程式 *  * 分析下面的程式可能會產生哪些問題? *   * 使用Vector或者Collections.synchronizedXXX * 分析一下,這樣能解決問題嗎? *  */package yxxy.c_024;import java.util.Vector;import java.util.concurrent.TimeUnit;public class TicketSeller2 {    static Vector<String> tickets = new Vector<>();            static {        for(int i=0; i<1000; i++) tickets.add("票 編號:" + i);    }        public static void main(String[] args) {                for(int i=0; i<10; i++) {            new Thread(()->{                while(tickets.size() > 0) {                                        try {                        TimeUnit.MILLISECONDS.sleep(10);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                                                            System.out.println("銷售了--" + tickets.remove(0));                }            }).start();        }    }}

運行結果:

我們看到程式出現了錯誤,所以雖然同步容器保證了方法的原子性,但可能會發生判斷與操作分離,整體操作不具備原子性

 

  三、並發容器

  1.並發容器支援多線程

  ConcurrentSkipListMap跳錶結構,他是排好序的容器執行插入操作較快

  ConcurrentHashMap將容器分成了16段,每次執行插入操作的時候只執行其中的一段,而HashTable為鎖定一整段

  ConcurrentLinkedQueue無界隊列,記憶體耗不完的情況下可以一直加(offer,add),拿(poll拿出來刪掉,peek拿出來不刪)

  BolckingQueue阻塞式隊列,加(put如果滿了就會等待),拿(take如果空了就會等待)

    幾個特殊的阻塞式隊列:

    1)TransferQueue轉移隊列,相較於其他隊列,多了一個transfer方法生產者將任務直接發給消費者,不進入結束隊列,在transfer找不到消費者情形下阻塞,即時處理用的較多,隊列還有一定容量

    2)DelayQueue計時任務隊列

    3)synchronusQueue同步隊列,特殊的transferQueue,沒有容量的隊列,不能調用add方法,只能調用put方法阻塞等待消費者消費,生產者生產的任務直接給消費者消費

 還是之前的售票程式,現在改用並發容器以ConcurrentLinkedQueue來重新實驗一遍

/** * 有N張火車票,每張票都有一個編號 * 同時有10個視窗對外售票 * 請寫一個類比程式 *  * 使用ConcurrentQueue提高並發性 */package yxxy.c_024;import java.util.Queue;import java.util.concurrent.ConcurrentLinkedQueue;import java.util.concurrent.TimeUnit;public class TicketSeller4 {    static Queue<String> tickets = new ConcurrentLinkedQueue<>();            static {        for(int i=0; i<1000; i++) tickets.add("票 編號:" + i);    }        public static void main(String[] args) {                for(int i=0; i<10; i++) {            new Thread(()->{                while(true) {                    String s = tickets.poll();                    if(s == null) break;                    else System.out.println("銷售了--" + s);                }            }).start();        }    }}

運行結果:

當我們改用並發容器ConcurrentLinkedQueue時,我們調用poll方法售票直至票數為0時,字串s會變為null,所以在接下來的if判斷後會跳出

當我們在此例中使用了並發容器時,就具有高的效率同時也不會出現問題

  四、其他常用同步容器

  1.Collocations.synchronizedXXX()將未加鎖的容器加鎖,在並發度不高的情況下可以使用此方法,在並發度較高且需排序的情況下使用concurrentSkipListMap

  2.CopyOnWriteList寫時複製連結資料表,在讀取時不加鎖,因此讀取的時候效率高,但寫的效率特別低,應用情境:在需要大量讀取少量寫入時使用

  五、總結

  1.對於map/set的使用

    無線程安全要求:hashmap、treemap、linkedhashmap

    並發度低:   hashtable、Collections.synchronizedXXX()

    並發度高:   concurrentHashMap、concurrentSkipListMap

  2.list

    無線程安全要求:ArrayList、LinkedList

    並發度低:   Collections.synchronizedXXX()

    並發度高:   Queue

              concurrentLinkedQueue

              BlockingQueue

                LinkedBQ

                ArrayBQ

                TransferQueue

                synchronusQueue

              DelayQueue

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.