Java多線程編程

1. synchronized

同步鎖,當多個線程爭搶該資源時候,同一時間只容許一個線程拿到.

2. volatile

在讀取改變量值時,讀取的均爲變量的最新值.

3. ThreadLocal 

用於在無狀態對象內,每個線程線程獨享的內存空間.常用cookie,connection之類的.

4. InheritableThreadLocal

與ThreadLocal類似,區別是InheritableThreadLocal可以讀取父線程的變量.

5. ReentrantLock 

重入鎖,可以多次鎖定與釋放.提供了鎖與釋放鎖的方法,可以實例化的時候,傳入參數指定是否是公平鎖.

重入鎖與同步鎖在線程間通信應用示例:

    private static String info = "";


    public static void main(String[] args) {

        //同步鎖
        testSyncLock();
        //重入鎖
        testReenLock();

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

    //同步鎖
    public static void testSyncLock() {
        Object lock = new Object();
        //線程2讀取info
        new Thread(() -> {
            synchronized (lock) {
                //10秒
                try {
                    long millis = System.currentTimeMillis();
                    lock.wait(5000);

                    System.out.println("同步鎖得到的信息:" + info + "消耗時間: " + (System.currentTimeMillis() - millis));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        //線程1設置info值,完成之後,通知線程2去取該值
        new Thread(() -> {
            synchronized (lock) {
                info = "同步鎖線程1設置信息....";
                //通知線程2,可以向下執行
                lock.notifyAll();
            }
        }).start();
    }

    //重入鎖
    public static void testReenLock() {
        ReentrantLock reentrantLock = new ReentrantLock(false);
        Condition condition = reentrantLock.newCondition();
        //線程2讀取info
        new Thread(() -> {
            try {
                long millis = System.currentTimeMillis();
                reentrantLock.lock();
                //等待被通知,最多等待5秒
                condition.await(10, TimeUnit.SECONDS);
                System.out.println("重入鎖得到的信息:" + info + "消耗時間: " + (System.currentTimeMillis() - millis));
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                //釋放鎖
                reentrantLock.unlock();
            }
        }).start();
        //線程1設置info值,完成之後,通知線程2去取該值

        new Thread(() -> {
            try {
                //線程阻塞,等待獲取到鎖
                reentrantLock.lock();
                //設置info值
                info = "重入鎖線程1設置信息....";
                //通知
                condition.signalAll();
            } finally {
                //釋放
                reentrantLock.unlock();
            }
        }).start();


    }

6.ReentrantReadWriteLock 

實現了讀鎖與寫鎖.類似於數據庫的S鎖(共享鎖)與X鎖(獨佔鎖).S鎖與S鎖之間可以共存,與X鎖排斥.對讀取數據加鎖,可以採用S鎖提供併發量.對修改數據,採用X鎖,保證數據的準確性,原子性.

代碼示例:

    public static void testRWLock(){
        ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
        //讀鎖
        ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
        readLock.lock();
        {
            //執行相關讀操作
        }
        //釋放
        readLock.unlock();

        //寫鎖
        ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
        writeLock.lock();
        {
            //執行相關寫操作
        }
        //釋放
        writeLock.unlock();
    }

7. ConcurrentHashMap 

使用類似於hashMap,但HashMap並不支持併發操作.HashMap在併發下,擴容時候,可能會出現環.因而get的時候,可能會陷入死循環.

在1.8中,ConcurrentHashMap 的性能有了進一步提升,拋棄原有的分塊加鎖機制,改爲對碰撞節點自旋CAS操作,保證原子性.

8. CopyOnWriteArrayList

多線程下的List.在add set,remove 時候,都會加上重入鎖.get時候不加.保證寫入原子性.

9. CopyOnWriteArraySet 同上 

10. CountDownLatch

倒計時鎖,在倒計時爲0時候,會發出notifyAll()類似消息.讓wait的線程運行起來.可以用於線程執行計算.

代碼示例:

public static void countDownLatchTest() {
        //10個線程執行完成,輸出全部執行完成
        CountDownLatch downLatch = new CountDownLatch(10);

        long now = System.currentTimeMillis();
        for (int i = 0; i < 10; i++) {
            //每個線程延時300ms
            new Thread(() -> {
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //執行完成,按下計數器
                    downLatch.countDown();
                }
            }).start();
        }

        try {
            //等待線程全部執行完
            while (downLatch.getCount() != 0){
                Thread.sleep(100);
            }
            System.out.println("線程全部執行完,耗時:" + (System.currentTimeMillis() - now));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

11. CyclicBarrier

循環等待,CyclicBarrier.await方法,只有當所有線程都處於等待時候,在執行下一步.就像賽馬場,馬匹都有一個門攔着,都ready的時候,纔可以起跑.也可以理解爲田徑場,所有選手跑完比賽,纔可以結束比賽.意義就是全部線程都在await,則自動釋放.與上面一個的區別是,上面一個計數爲0時,開始通知.

示例:

 public static void cycTest(){
        CyclicBarrier lock = new CyclicBarrier(10) ;
        for (int i = 0; i < 10; i++) {
            //每個線程延時300ms
            int finalI = i;
            new Thread(() -> {
                try {
                    lock.await() ;
                    //這裏線程應該併發
                    System.out.println("開始起跑" + finalI);
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
        try {
            //等待500ms
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

12. Semaphore 

可以用於線程容積控制,當前可以執行的線程數,就類比於公共廁所,茅坑總數多少,當前多少正在使用,如果都是用,那就需要等待.使用完就釋放.

public static void semaphoreTest() {
        //容積3,也就是同事執行的線程只能有3個
        Semaphore semaphore = new Semaphore(3) ;
        for (int i = 0; i < 10; i++) {
            //每個線程延時300ms
            int finalI = i;
            new Thread(() -> {
                try {
                    //請求資源
                    semaphore.acquire();
                    //這裏線程應該併發
                    System.out.println("開始起跑" + finalI);
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    //釋放資源
                    semaphore.release();
                }
            }).start();
        }
    }

13. Ato*類

該類下的操作都是原子性的,調用getAndSet方法,會一直進行cas操作,知道寫入.

 

單機下的併發操作,目前基本用處很少,現在使用場景基本都是集羣下的併發.集羣下的併發,一般首先需要先處理集羣的併發,可以利用zookeeper,redis,memcache等框架,然後單機下多線程併發處理,才能使用到某些.具體使用場景,可能需要反覆驗證,猜想檢測.

 

 

 

 

 

 

 

 

 

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