它允许多个线程等待直到在其他线程中一组操作执行完成
CountDownLatch初始化的时候需要传入参数N
await()方法能够阻塞线程直到调用N次的countDown()后才会释放线程
countDown()可以在多个线程中调用,每次调用N减一
可以将CountDownLatch理解为一种特殊的共享锁,每次countDown()后会释放锁,当所有的线程释放完锁后,执行await()方法的线程,会抢占此锁。
public class Test_CountDownLatch {
/*
没隔1s开启一个线程,共开启6个线程
若希望6个线程 同时 执行某一操作
可以用CountDownLatch实现
*/
public static void test01() throws InterruptedException {
CountDownLatch ctl = new CountDownLatch(6);
for (int i=0; i<6; i++) {
new Thread() {
@Override
public void run() {
ctl.countDown();
try {
ctl.await();
System.out.println("here i am..");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
Thread.sleep(1000);
}
}
/*
开启6个线程,main线程希望6个线程都执行完某个操作后,才执行某个操作
可以用CountDownLatch来实现
*/
public static void test02() throws InterruptedException {
CountDownLatch ctl = new CountDownLatch(6);
for (int i = 0; i<6 ; i++) {
new Thread() {
@Override
public void run() {
System.out.println("after print..");
ctl.countDown();
}
}.start();
Thread.sleep(1000);
}
ctl.await();
System.out.println("main thread do something ...");
}
public static void main(String args[]) throws InterruptedException {
test01();
}
}
手写一个简单的CountDownLatch类
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
public class TomCountDownlatch {
private Sync sync;
public TomCountDownlatch(int count) {
sync = new Sync(count);
}
public void countDown() {
sync.releaseShared(1);
}
public void await() {
sync.acquireShared(1);
}
class Sync extends AbstractQueuedSynchronizer {
public Sync(int count) {
setState(count);
}
@Override
protected int tryAcquireShared(int arg) {
//当state变为0的时候,加锁成功
return getState()==0 ? 1:-1;
}
@Override
protected boolean tryReleaseShared(int arg) {
for (;;){
int c = getState();
if (c == 0 ){
return false;
}
int nextc = c-1;
if (compareAndSetState(c,nextc)) {
return nextc == 0;
}
}
}
}
}
Semaphore是一个计数信号量,常用于限制可以访问某些资源(物理或者逻辑的)线程数目
简单来说是一种控制并发量的共享锁
应用场景:数据库限流等
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
public class TomCountDownlatch {
private Sync sync;
public TomCountDownlatch(int count) {
sync = new Sync(count);
}
public void countDown() {
sync.releaseShared(1);
}
public void await() {
sync.acquireShared(1);
}
class Sync extends AbstractQueuedSynchronizer {
public Sync(int count) {
setState(count);
}
@Override
protected int tryAcquireShared(int arg) {
//当state变为0的时候,加锁成功
return getState()==0 ? 1:-1;
}
@Override
protected boolean tryReleaseShared(int arg) {
for (;;){
int c = getState();
if (c == 0 ){
return false;
}
int nextc = c-1;
if (compareAndSetState(c,nextc)) {
return nextc == 0;
}
}
}
}
}
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
public class TomSemaphore {
private Sync sync;
public TomSemaphore(int permits) {
sync = new Sync(permits);
}
public void acquire() {
sync.acquireShared(1);
}
public void release() {
sync.releaseShared(1);
}
class Sync extends AbstractQueuedSynchronizer {
int permits;
public Sync(int permits) {
this.permits = permits;
}
//加锁
@Override
protected int tryAcquireShared(int arg) {
int state = getState();
int nextState = state + arg;
//如果信号量没占满,加锁的个数没有达到permits
if (nextState <= permits){
if (compareAndSetState(state, nextState))
return 1;
}
return -1;
}
//解锁
@Override
protected boolean tryReleaseShared(int arg) {
int state = getState();
if (compareAndSetState(state,state-arg)){
return false;
} else {
return true;
}
}
}
}
循环栅栏,可以循环利用的屏障。
举例:排队上摩天轮,每到齐四个人,就可以上同一车厢。
简单使用:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.locks.LockSupport;
public class Test_CyclicBarrier {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(4,
new Thread(){
@Override
public void run() {
System.out.println("==========>>>>>>>>>栅栏");
}
});
for (int i=0; i<100; i++) {
new Thread() {
@Override
public void run() {
try {
barrier.await();
System.out.println("上到摩天轮");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}.start();
LockSupport.parkNanos(1000 * 1000 * 10000);
}
}
}
原理解析:使用可重入锁将其挂起,并使用全局分代和局部分代进行对比来确认批次从而完成唤醒操作。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class TomCyclicBarrier {
private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
//记录当前这个批次有多少个
private int count = 0;
//记录批次大小
private final int parties;
//分代
private Object generation = new Object();
public TomCyclicBarrier(int parties) {
if (parties <= 0) {
throw new IllegalArgumentException();
}
this.parties = parties;
}
//进入下一个分代
public void nextGeneration() {
condition.signalAll();
count = 0;
generation = new Object();
}
public void await() {
//时间排队,需要将线程放入等待队列
//还需要将线程挂起
final ReentrantLock lock = this.lock;
lock.lock();
try {
//记录当前的generation,相当于记录当前批次的id
final Object g = generation;
int index = ++ count;
//批次已经达到parties
if(index == parties) {
//进入下一批次
nextGeneration();
return;
}
//若未达到批次,就进入等待
for (;;) {
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (g != generation) {
return;
}
}
} finally {
lock.unlock();
}
}
}
开启一个新的线程本质上去讲只有一种方法->new Thread(){}.start();,虽然我们平时可以使用多种方式去实现如:Runnable、Callable
import java.util.concurrent.locks.LockSupport;
public class Demo2_RunnableTest {
public static void main(String[] args) {
Runnable cmd = new Task();
cmd.run();
System.out.println("5s 输出消息");
Thread th = new Thread(cmd);
th.start();
System.out.println("开启线程,实现异步,立即打印消息....");
}
}
class Task implements Runnable {
@Override
public void run() {
System.out.println("run 方法开始执行");
LockSupport.parkNanos(1000*1000*1000*5l);
}
}
Callable相比于Runnable,可以获取结果
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.locks.LockSupport;
public class Demo3_CallableTest {
public static void main(String[] args) throws InterruptedException, ExecutionException {
CallTask cTask =new CallTask();
//futureTask只能执行一次
FutureTask fTask = new FutureTask(cTask);
Thread th = new Thread(fTask);
th.start();
System.out.println((String)fTask.get());
}
}
class CallTask implements Callable {
@Override
public String call() throws Exception {
LockSupport.parkNanos(1000*1000*1000*5L);
System.out.println("done....");
return "11";
}
}
FutureTask
import java.util.concurrent.Callable;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
public class TomFutureTask implements Runnable {
//future只能执行一次,简单的分为3种状态,实际更多
private volatile int state = NEW;
private static final int NEW = 0;
private static final int RUNNING = 1;
private static final int FINISED = 2;
//返回的结果
private T result;
//要自行的task
Callable callable;
public TomFutureTask(Callable task) {
this.callable = task;
}
//获取结果的线程等待队列
LinkedBlockingQueue waiters = new LinkedBlockingQueue<>(100);
//执行当前futureTask的线程,用cas进行争抢
AtomicReference runner = new AtomicReference<>();
@Override
public void run() {
if (state != NEW || runner.compareAndSet(null,Thread.currentThread())) {
return;
}
state=RUNNING;
try {
result = callable.call();
} catch (Exception e) {
e.printStackTrace();
} finally {
state = FINISED;
}
//方法执行完,唤醒所有线程
while (true) {
Thread waiter = waiters.poll();
if (waiter == null) {
break;
}
LockSupport.unpark(waiter);
}
}
public T get() {
if (state != FINISED) {
waiters.offer(Thread.currentThread());
}
while (state != FINISED) {
LockSupport.park();
}
return result;
}
}
ForkJoinPool是ExecutorService接口的实现,它专为可以递归分解成小块的工作而设计。
fork/join框架将任务分配给线程池中的工作线程,充分利用多处理器的优势,提高程序性能。
使用fork/join框架的第一步是编写执行一部分工作的代码。类似的伪代码如下:
如果(当前工作部分足够小)
直接做这个工作
其他
把当前工作分成两部分
调用这两个部分并等待结果
将此代码包装在ForkJoinTask子类中,通常是RecursiveTask(可以返回结果)或者RecursiveAction。
分解任务fork出新任务,汇集join任务执行结果。
使用实例:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.RecursiveTask;
public class ForkJoinTest {
static ArrayList urls = new ArrayList(){
{
add("http://www.baidu.com");
add("http://www.sina.com");
add("http://www.baidu.com");
···
add("http://www.baidu.com");
add("http://www.sina.com");
add("http://www.baidu.com");
add("http://www.sina.com");
add("http://www.baidu.com");
add("http://www.sina.com");
add("http://www.baidu.com");
add("http://www.sina.com");
add("http://www.baidu.com");
add("http://www.sina.com");
add("http://www.baidu.com");
add("http://www.sina.com");
add("http://www.baidu.com");
add("http://www.sina.com");
add("http://www.baidu.com");
add("http://www.sina.com");
}
};
static ForkJoinPool firkJoinPool = new ForkJoinPool(3,
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null,
true);
public static void main(String[] args) {
Job job = new Job(urls, 0, urls.size());
ForkJoinTask forkJoinTask = firkJoinPool.submit(job);
String result = forkJoinTask.get();
System.out.println(result);
}
public static String doRequest(String url) {
return "Kody ... test ... " + url + "\n";
}
static class Job extends RecursiveTask {
List urls;
int start;
int end;
public Job(List urls, int start, int end) {
this.urls = urls;
this.start = start;
this.end = end;
}
@Override
protected String compute() {
//计算任务的大小
int count = end - start;
if (count <= 10) {
//直接执行
String result = "";
for (int i = start; i
适用:使用尽可能少的线程池-在大多数情况下,最好的决定是为每一个应用程序或系统使用一个线程池,如果不需要特定的调整,请使用默认的公共线程池。使用合理的阈值将ForkJoinTask拆分为子任务,避免在ForkJoinTask中出现任何阻塞
适合数据处理、结果汇总、统计等场景;
java8实例:java.util.Arrays类用于其parallelSort()方法