java多線程 —— 多線程同步器

1、什麼是多線程同步器?

可以理解爲,用於控制多線程之前同步動作的工具。

2、爲什麼使用多線程同步器?

在實際應用中,我們希望多線程根據某些一些特定的規則執行。因此有了多線程同步器,通過不同的多線程同步器,可以讓多線程實現多樣的行爲。

3、多線程同步器介紹

3.1、Samaphore

應用場景:對於一組有限制都資源訪問。比如餐廳有2個位置但同時有10個人要喫飯,則要控制10個人對餐位的併發實用。即當餐位有空餘時,就會有人用餐,沒有空餘時,則進入阻塞等待。

用法:定義Semaphore變量semaphore包含受限的資源個數,每個人要來用餐時先調用semaphore.acquire()方法獲取一個餐位(若沒有餐位,則阻塞等待),用完餐後調用semaphore.release()釋放餐位給其它人用。

package study.threadPoolTest;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class SamaphoreTest {
    private static Semaphore semaphore = new Semaphore(2);

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        for (int i = 0; i < 10; i++) {
            Person p = new Person(i);
            executorService.submit(p);
        }
        executorService.shutdown();

    }

    static class Person implements Callable {

        int i;

        Person(int i) {
            this.i = i;
        }

        @Override
        public Object call() throws Exception {
            semaphore.acquire();
            System.out.println("start:" + i);
            Thread.sleep(2000);
            System.out.println("finished:" + i);
            semaphore.release();
            return null;
        }
    }
}

結果:


semaphore的值爲2,即最多同時只有2個線程在執行。
start:0
start:1
兩個線程執行。
當且僅當
finished:0
第0號線程結束了,第2號線程纔開始
start:2


start:0
start:1
finished:0
start:2
finished:1
start:3
finished:2
start:4
finished:3
start:5
finished:4
start:6
finished:5
start:7
finished:6
start:8
finished:7
start:9
finished:8
finished:9

3.2、CountDownLatch

應用場景:等待一組線程任務完成後在繼續執行當前線程。

用法:定義一個CountDownLatch變量latch,在當前線程中調用latch.await()方法,在要等待的一組線程中執行完後調用latch.countDown()方法,這樣當該做線程都調用過latch.countDown()方法後就開始執行當前線程latch.await()後的方法。

package study.threadPoolTest;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CountDownLatchTest {

    private static CountDownLatch countDownLatch = new CountDownLatch(2);
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        executorService.submit(new mainThread());
        for (int i = 0;i<5;i++){
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                System.out.println("Exception" + e);
            }
            int j = i;
            executorService.submit(() -> {
                System.out.println("start" + j);
                countDownLatch.countDown();
                System.out.println("finished " + j);

            });
        }
        executorService.shutdown();
    }
    static class mainThread implements Runnable{

        @Override
        public void run() {
            try {
                System.out.println("start");
                countDownLatch.await();
                System.out.println("finished");
            } catch (InterruptedException e) {
                System.out.println("Exception" + e);
            }
        }
    }

}

結果:


countDownLatch的值爲2,表示,要等兩個線程執行了之後,才繼續執行。
start
主線程start之後,等在countDownLatch.await();
start0
finished 0
start1
finished 1
當兩個線程執行了,主線程繼續執行
finished


start
start0
finished 0
start1
finished 1
finished
start2
finished 2
start3
finished 3
start4
finished 4

3.3、CyclierBarrier

應用場景:等待一組線程到達某個點後一起執行,該組線程達到指定點後可以再次循環執行。也可用於一組線程達達某個點後再執行某個方法。

用法:定義一個CyclicBarrier變量barrier,線程達到某個約定點時調用barrier.await()方法,當該組所有線程都調用了barrier.await()方法後改組線程一起向下執行。

CyclicBarrier和CountDownLatch的區別

CountDownLatch的計數器只能使用一次。而CyclicBarrier的計數器可以使用reset() 方法重置。所以CyclicBarrier能處理更爲複雜的業務場景,比如如果計算髮生錯誤,可以重置計數器,並讓線程們重新執行一次。
CyclicBarrier還提供其他有用的方法,比如getNumberWaiting方法可以獲得CyclicBarrier阻塞的線程數量。isBroken方法用來知道阻塞的線程是否被中斷。

package study.threadPoolTest;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CyclicBarrierTest {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
        for (int i = 0;i < 10;i++){
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int finalI = i;
            executorService.submit(() -> {
                try {
                    System.out.println("start" + finalI + "count=" + cyclicBarrier.getNumberWaiting());
                    cyclicBarrier.await();
                    System.out.println("finished" + finalI);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }

            });
        }
        executorService.shutdown();
    }
}

結果:


CyclierBarrier的值爲3,表示每次都會攔截線程,攔截到3個線程,才繼續執行。且CyclierBarrier一直有效
start3count=0
start4count=1
start5count=2
finished5
finished3
finished4
start6count=0
start7count=1
start8count=2
finished8
finished6
finished7
start9count=0


start0count=0
start1count=1
start2count=2
finished2
finished0
finished1
start3count=0
start4count=1
start5count=2
finished5
finished3
finished4
start6count=0
start7count=1
start8count=2
finished8
finished6
finished7
start9count=0

 

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