JUC 基本操作

 

一、JUC-TimeUnit枚舉

TimeUnit是  java.util.concurrent 中的一個枚舉類(時間單元類)。一般讓進行控制線程睡眠時使用。

TimeUnit提供了可讀性更好的線程暫停操作,通常用來替換Thread.sleep(),相比 Thread.sleep()方法的一個好處就是 TimeUnit可以設置時間單位。

這個類支持有:日(DAYS)、時(HOURS)、分(MINUTS)、秒(SECONDS)、毫秒(MILLISECONDS)、微秒(MICROSECONDS)、納秒(NANOSECONDS)。

    

1、實例:進行休眠控制,休眠2秒

    使用Thread.sleep() 方法處理

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("[sleep start]"+System.currentTimeMillis());
                try {
                    Thread.sleep(2*1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("[sleep end]"+System.currentTimeMillis());
            }
        }).start();
    }

    直接使用TimeUnit類來處理

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("[sleep start]"+System.currentTimeMillis());
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("[sleep end]"+System.currentTimeMillis());
            }
        }).start();
    }

2、時間單位的轉換

在 TimeUnit類中最爲重要的特點是可以方便的進行各種時間單位的轉換,它提供了一個convert()方法

實例:1小時轉換爲秒數

    public static void main(String[] args) {
        long sec = TimeUnit.SECONDS.convert(1, TimeUnit.HOURS);
        System.out.println("1小時轉換爲秒數:" + sec);

        long minutes = TimeUnit.MINUTES.convert(sec, TimeUnit.SECONDS);
        System.out.println(sec + "秒有轉換爲分鐘:" + minutes);
    }

二、JUC-Atomic Variables(原子變量)

 簡單使用:volatile關鍵字與內存可見性

 

三、JUC-Concurrent Collections(併發容器)

JUC裏的同步容器類

1、java 基礎數據集合容器中

線程安全與非線程安全的對象如下

Collection  |--List |--ArrayList
            |       |--LinkList
            |       |--Vector -->線程安全
            |
            |
            | --Set |--TreeSet
                    |--HashSet
Map |--TreeMap
    |--HashMap
    |--HashTable -->線程安全

StringBuffer  -->線程安全
StringBulider -->非線程安全

解決這些非線程安全的集合的線程安全

通過使用的 Collections.synchronizedXXX()方法來轉換。HashMap的話可以直接使用HashTable轉換

    

(1)List用線程安全對象處理,map同理

public class CollectionDemo {

    private static Vector list = new Vector();
//    private static List<Integer> list = new ArrayList();
//    private static List<Integer> list = Collections.synchronizedList(new ArrayList<>());

    public static void main(String[] args){
        //讓50個線程去執行
        for (int i = 0; i < 50; i++) {
            new MyThread().start();
        }
    }

    private static class MyThread extends Thread{
        @Override
        public void run() {            
            for (int i = 0; i < 100; i++) {
                list.add(i);
            }
            System.out.println("集合大小:--->"+list.size());//預期的集合大小應該是50x100=5000
        }
    }

}

(2)讀寫處理

public class CollectionDemo {

    private static List<Object> list = Collections.synchronizedList(new ArrayList<>());

    public static void main(String[] args){
        for (int i = 0; i < 10; i++) {
            new MyThread().start();
        }
    }

    private static class MyThread extends Thread{
        @Override
        public void run() {
            Iterator<Object> iterator = list.iterator();
            while (iterator.hasNext()){
                System.out.println(iterator.next());
            }
            list.add("add" + Thread.currentThread().getName());
        }
    }

}

    

注意:上面的解決方法,一是程序效率低;二是在複合操作的時候會報併發修改異常(ConcurrentModificationException)。
 

2、JUC 解決非線程安全的集合類

JUC裏面提供了一系列同步容器類用來解決非線程安全的集合類,我們只需要在多線程併發編程中,用這些同步容器類類替換掉原來的HashMap,ArrayList,HashSet集合,就可以了保證即是線程安全的,比使用 Collections.synchronizedXXX()的效率高。

HashMap -- ConcurrentHashMap

TreeMap -- ConcurrentSkipListMap

ArrayList -- CopyOnWriteArrayList

ArraySet -- CopyOnWriteArraySet等等

CopyOnWriteArrayList和CopyOnWriteArraySet 每次存入要新建一個存儲結構,寫操作效率低比Vector都低,浪費空間,用於寫得少,讀得多。

public class CollectionDemo {

    private static CopyOnWriteArrayList<Object> list = new CopyOnWriteArrayList<>();

    public static void main(String[] args){
        for (int i = 0; i < 5; i++) {
            new MyThread().start();
        }
    }

    private static class MyThread extends Thread{
        @Override
        public void run() {
            Iterator<Object> iterator = list.iterator();
            while (iterator.hasNext()){
                System.out.println(Thread.currentThread().getName() + "==" + iterator.next());
            }
            list.add("add" + Thread.currentThread().getName());
        }
    }

}

    

四、JUC-Synchronizers(同步器)

簡單瞭解三個類用於同步一批線程的行爲,分別是CountDownLatch、Semaphore和CyclicBarrier。

1、CountDownLatch類

CountDownLatch是一個計數器閉鎖,它允許一個或多個線程等待直到在其他線程中一組操作執行完成。CountDownLatch用一個給定的計數器來初始化,該計數器的操作是原子操作。

例如:爲了讓主線程等待工作線程執行完成,主線程調用await操作讓主線程阻塞,當工作線程完成初始化過程之後,每當一個線程完成了自己的任務後,計數器的值就會減1。當計數器值到達0時,它表示所有的線程已經完成了任務,然後在閉鎖上等待的線程就可以恢復執行任務。

public class CountDownLatchDemo {

    public static void main(String[] args) {
        // 創建 CountDownLatch, 3個線程任務
        CountDownLatch countDownLatch = new CountDownLatch(3);
        LatchDemo latchDemo = new LatchDemo(countDownLatch);
        for (int i = 0; i < 3; i++) {
            new Thread(latchDemo).start();
        }

        try {
            // 主線程執行await方法,進行等待,知道計數器的值爲0時繼續向下執行
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("主線程運行完成");

    }

    public static class LatchDemo implements Runnable{
        private CountDownLatch countDownLatch = null;

        public LatchDemo(CountDownLatch countDownLatch) {
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            try {
                for (int i = 0; i <5; i++) {
                    System.out.println(Thread.currentThread().getName() + "==" + i);
                }
            } finally {
                // 計數器減1
                countDownLatch.countDown();
            }
        }
    }
}

2、Semaphore類

Semaphore是一個控制訪問多個共享資源的計數信號量,用於管理一組資源。它相當於控制使用公共資源的活動線程的數量。在信號量上的兩種操作:

  • acquire(獲取) 當一個線程調用acquire操作時,它可以成功獲取信號量(信號量減1),如果沒有就等待。
  • release(釋放)釋放信號量(信號量加1),然後喚醒等待的線程。acquire 之後拋異常,信號量不會自動歸還,所以儘量放到 finally 塊中, 防止信號量流失。

例如:一個停車場有5個車位,假設有10臺車進來停車,一個車位同時只能被一臺車使用,只有車使用完開走了,其他車才能繼續使用。

public static void main(String[] args) {
        // 5個車位
        Semaphore semaphore = new Semaphore(5);

        for (int i = 1; i <= 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        semaphore.acquire();
                        System.out.println(Thread.currentThread().getName() + "搶到車位了");
                        TimeUnit.SECONDS.sleep(1);
                        System.out.println(Thread.currentThread().getName() + "離開了");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        semaphore.release();
                    }
                }
            }).start();
        }
    }

3、CyclicBarrier類

CyclicBarrier 的字面意思是迴環柵欄/可循環使用(Cyclic)的屏障(Barrier),通過它可以實現讓一組線程等待至某個狀態之後再全部同時執行。

它允許一組線程到達某個公共屏障點 (common barrier point)時被阻塞,直到最後一個線程到達屏障(同步點)時,屏障纔會開門,所有被屏障攔截的線程都到齊了纔會繼續幹活。因爲該 barrier 在釋放等待線程後可以重用,所以稱它爲循環 的 barrier。

    

實例:週末5人組織大巴去旅遊,總共有兩個景點,每個景點約定好遊玩時間,一個景點結束後需要集中一起出發到下一個景點。

public class CyclicBarrierDemo {

    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable(){
            // 當所有線程到達barrier時執行
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "最後一個到達,人齊了!");
            }
        });

        BarrierDemo barrierDemo = new BarrierDemo(cyclicBarrier);
        for (int i = 0; i < 5; i++) {
            new Thread(barrierDemo).start();
        }


    }

    public static class BarrierDemo implements Runnable {
        private CyclicBarrier cyclicBarrier = null;

        public BarrierDemo(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            try {
                TimeUnit.SECONDS.sleep(1);
                System.out.println(Thread.currentThread().getName() + "到達景點1");
                cyclicBarrier.await();// 線程在這裏等待,直到所有線程都到達barrier。
                TimeUnit.SECONDS.sleep(1);
                System.out.println(Thread.currentThread().getName() + "到達景點2" );
                cyclicBarrier.await();// 線程在這裏等待,直到所有線程都到達barrier。
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
            }
        }
    }
}

    

 

五、JUC- Exchanger(線程之間數據交換)

Exchanger用於進行線程間的數據交換,它提供一個同步點,在這個同步點,兩個線程可以交換彼此的數據

  • 兩個線程通過Exchanger.exchange(obj)方法交換數據,如果一個線程先執行exchange方法,它會一直等待第二個線程也執行exchange方法
  • 當兩個線程都達到同步點時,這兩個線程就可以交換數據,將本線程生產出來的數據傳遞給對方(只能在兩個線程之間交換數據)
public class ExchangerDemo {
    public static void main(String[] args) {
        Exchanger<String> exchanger = new Exchanger<>();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    String a = "A數據";
                    String exchangeData = exchanger.exchange(a); //交換我自己的數據,並且獲取別人的數據
                    System.out.println("線程a:" + exchangeData);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    String b = "b數據";
                    String exchangeData = exchanger.exchange(b); //交換我自己的數據,並且獲取別人的數據
                    System.out.println("線程b:" + exchangeData);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

    

 

參考文章: JUC回顧之-AQS同步器的實現原理:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章