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