一、CountDownLatch
1、看代码
代码示例1:
package com.nipx.demo.CAS;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
public class CountDownLatch_demo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
for (int i = 0; i < 10; i++) {
int n = i;
new Thread(new Runnable() {
@Override
public void run() {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(String.format("运动员%d起跑", n));
}
}).start();
}
System.out.println("预备。。。");
Thread.sleep(3000L);
latch.countDown();
System.out.println("跑");
}
}
运行结果:
代码示例2:
package com.nipx.demo.CAS;
import java.util.concurrent.CountDownLatch;
public class CountDownLatch_demo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(10);
for (int i = 10; i > 0; i--) {
int n = i;
new Thread() {
@Override
public void run() {
System.out.println(n);
latch.countDown();
}
}.start();
Thread.sleep(1000L);
}
latch.await();
System.out.println("点火");
}
}
运行结果
CountDownLatch是一个计数器,配合await()方法将所有线程挂起,当CountDownLatch减为0时,唤醒所有的线程。
2、手写CountDownLatch的实现
package com.nipx.demo.CAS;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
public class CountDownLatch_demo {
private Sync sync;
public CountDownLatch_demo(int count) {
sync = new Sync(count);
}
public void await() {
sync.tryAcquireShared(1);
}
public void countDown() {
sync.tryReleaseShared(1);
}
class Sync extends AbstractQueuedSynchronizer {
//count就表示初始的count值
public Sync(int count) {
setState(count);
}
@Override
protected int tryAcquireShared(int count) {
return getState() == 0 ? 1 : -1;
}
@Override
protected boolean tryReleaseShared(int arg) {
//自旋
for (; ; ) {
//获取count值
int c = getState();
//如果count等于0,返回false
if (c == 0) {
return false;
}
//减1操作
int nextCount = c - 1;
//进行cas操作,如果cas成功并且nextCount==0 则返回true
if (compareAndSetState(c, nextCount)) {
return nextCount == 0;
}
}
}
}
}
countDownLatch只是继承了AQS抽象类,实现了AQS中的一些方法而已。
二、Semaphore
1、Semaphore是一个技术信号量,常用于限制可以访问某些资源的线程数量,就是一种用来控制并发量的共享锁。限流。
上代码:
package com.nipx.demo.CAS;
import java.util.concurrent.Semaphore;
public class Semaphore_demo {
//初始信号量为5,所有线程争抢信号量,没有抢到的到等待池等待
static Semaphore sem = new Semaphore(5);
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
//抢一个信号,也就是说得到一个锁通道
sem.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
//业务逻辑
System.out.println("这里写业务逻辑");
//释放信号量
sem.release();
}
}).start();
}
}
}
2、手动实现一个Semaphore,也是通过AQS,可见AQS的强大之处。
【注意别导错jar包】
package com.nipx.demo.CAS;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
public class NpxSemaphore {
private Sync sync;
public NpxSemaphore(int permits) {
this.sync = new Sync(permits);
}
//拿到信号量
public void acquire() {
sync.tryAcquireShared(1);
}
//释放信号量
public void release() {
sync.tryReleaseShared(1);
}
class Sync extends AbstractQueuedSynchronizer {
//信号量的最大值
private int permits;
public Sync(int permits) {
this.permits = permits;
}
//获取共享锁
@Override
protected int tryAcquireShared(int arg) {
//现在有多少个线程已经获取了信号量
int state = getState();
int nextState = state + arg;
//如果还有剩余信号量,争抢
if (nextState < permits) {
if (compareAndSetState(state, nextState)) {
return 1;
}
}
return -1;
}
//释放信号量
@Override
protected boolean tryReleaseShared(int arg) {
//现在有多少个线程已经获取了信号量
int state = getState();
//释放信号量,成功返回true
//本质就是进行cas释放一个共享锁
if (compareAndSetState(state, state - arg)) {
return true;
} else {
return false;
}
}
}
}
三、CyclicBarrier
1、循环栅栏,可以循环利用的屏障,采用ReentrantLock来实现
例如:坐缆车,4个座位坐满就可以开车。
上代码:
package com.nipx.demo.CAS;
import java.util.concurrent.locks.Condition;
public class NpxCyclicBarrier {
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 NpxCyclicBarrier(int parties) {
if (parties <= 0) {
throw new IllegalArgumentException();
}
this.parties = parties;
}
public void await() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
final Object g = generation;
int index = ++count;
//计数到齐
if (index == parties) {
//进入下一个批次
nextGeneration();
return;
}
//计数没有到齐
for (; ; ) {
condition.await();
if (g != generation) {
return;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
private void nextGeneration() {
condition.signalAll();
count = 0;
generation = new Object();
}
}
四、FutureTask
1、Runnable可以帮助我们执行一个线程,但是它的返回值只是void,那么如果我们想得到线程的返回值,我们应该怎们获取呢?使用FutureTask,首先我们看一个测试案例。
代码如下:
package com.nipx.demo.CAS;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.locks.LockSupport;
public class FutureTaskTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableTask t = new CallableTask();
FutureTask futureTaskTest = new FutureTask(t);
//执行
new Thread(futureTaskTest).start();
System.out.println(futureTaskTest.get());
}
}
class CallableTask implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("执行任务。。。。");
//模拟消耗时间
LockSupport.parkNanos(1000 * 1000 * 1000 * 5L);
return "success";
}
}
运行结果:
由此可见FutureTask只是对线程进行了一个封装,但是当它去拿取结果的时候是阻塞的,因为线程如果没有执行结束,这个时候是没有返回结果的,所以FutureTask阻塞等待获取结果。而且一个FutureTask只能用一次,也就是这个结果只能获取一次。
2、FutureTask的底层是如何实现的呢?
这个FutureTask分重要的三部分。第一部分是这个FutureTask;第二部分线程去执行FutureTask,第三部分线程是去获取FutureTask的返回结果;
看代码:
package com.nipx.demo.CAS;
import java.util.concurrent.Callable;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
public class NpxFutureTask<T> implements Runnable {
//初始状态
private volatile int state = NEW;
//记录线程状态
private static final int NEW = 0;
private static final int RUNNING = 1;
private static final int FINISHED = 2;
private static final int ERROR = 3;
private Callable<T> callable;
AtomicReference<Thread> runner = new AtomicReference<>();
//执行结果
T result;
//等待队列
LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue<Thread>();
public NpxFutureTask(Callable<T> call) {
this.callable = call;
}
@Override
public void run() {
//如果不是第一次执行,如果没有抢成功
if (state != NEW ||
!runner.compareAndSet(null, Thread.currentThread())) {
return;
}
state = RUNNING;
try {
result = callable.call();
state = FINISHED;
} catch (Exception e) {
state = ERROR;
}
//有结果之后就可以唤醒那些在等待池等待的线程来获取结果
while (true) {
Thread th = waiters.poll();
if (th == null) {
break;
}
LockSupport.unpark(th);
}
}
public T get() {
//当线程还没有执行完毕,那么就应该把获取结果的线程放到等待队列
if (state < 2) {
//放到等待队列是为了将线程挂起
waiters.offer(Thread.currentThread());
}
while (state < 2) {
//挂起线程,防止伪唤醒,用while
LockSupport.park();
}
return result;
}
}
换成自己的FutureTask,运行结果是一样的。