可以理解为,用于控制多线程之前同步动作的工具。
在实际应用中,我们希望多线程根据某些一些特定的规则执行。因此有了多线程同步器,通过不同的多线程同步器,可以让多线程实现多样的行为。
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