J.U.C包中提供了一些很是有用的工具类。在合适的场景下使用它们每每可以达到事半功倍的效果。好比Atomic工具类、Exchanger、CountDownLatch、CyclicBarrier、Semaphore这些。java
Atomic工具类可以实现原子操做数据。从数据类型的角度来看,能够分为:基本数据类型、数组、引用类型、引用类型属性的原子更新操做。它的底层原理其实就是对于Unsafe类和volatile的封装。
像AtomicInteger、AtomicLong等内部源码都是差很少的。值得注意的是像AtomicBoolean/AtomicChar等的实现原理实际上是内部将boolean、char、byte、等数据类型向上转型为了int类型,本质上和AtomicInteger差异不大。数据库
Exchanger能够交换线程间的数据,是一个线程间协做工做的工具类。它提供了一个同步点,用于线程间交换数据。Exchanger只能交换2个线程间的数据。理应如此:生活中,不管是交换利益、交换物品,这些行为都是发生在二者之间。
在实际的场景中,Exchanger十分适用于两个任务之间有数据上的相互依赖的场景。好比交易系统中的对帐功能。咱们以一个实际例子来看一看Exchanger的使用:数组
public class ExchangerTest {
public static final Exchanger<Integer> transExchanger = new Exchanger<>();
public static void main(String[] args) {
Thread th1 = new Thread(() -> {
// 伪代码
Integer transA = 1000;
try {
Integer transFromB = transExchanger.exchange(transA);
System.out.println("我是系统A:我系统中统计的交易额为:" + transA + " 我在交易系统中产生的交易额为:" + transFromB);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread th2 = new Thread(() -> {
// 伪代码
Integer transB = 1001;
try {
Integer transFromA = transExchanger.exchange(transB);
System.out.println("我是交易系统:系统A统计出的交易额为:" + transB + " 系统A实际在我这里产生的交易额为:" + transFromA);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
th1.start();
th2.start();
}
}
复制代码
执行结果以下:网络
我是交易系统:系统A统计出的交易额为:1001 系统A实际在我这里产生的交易额为:1000
我是系统A:我系统中统计的交易额为:1000 我在交易系统中产生的交易额为:1001多线程
CountDownLatch的做用是用于多线程间同步完成任务。值得注意的是它只能使用一次,缘由在于CountDownLatch设置锁资源以后,只提供了countDown()方法来释放锁资源,和await()方法来等待锁资源释放完毕。并无重置锁资源的功能。若是咱们理解AQS源码的话,那么读源码和对于CountDownLatch的使用就再简单不过了。并发
public class CountDownLatchTest {
public static final CountDownLatch countDownLatch = new CountDownLatch(2);
public static void main(String[] args) {
Thread th1 = new Thread(() -> {
System.out.println("th1 run.");
countDownLatch.countDown();
});
Thread th2 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("th2 run.");
countDownLatch.countDown();
});
Thread th3 = new Thread(() -> {
System.out.println("th3 waiting until count down 0.");
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("th3 last to run.");
});
th1.start();
th2.start();
th3.start();
}
}
复制代码
执行结果:框架
th1 run.
th3 waiting until count down 0.
th2 run.
th3 last to run.工具
因为CountDownLatch只能使用一次,所以它适用于那些一次性任务。好比一些框架的初始化。ui
CyclicBarrier一般被翻译为回栏栅。它的功能实际上是相似于CountDownLatch。不一样之处在于:spa
咱们分析一个例子以下:
public class CyclicBarrierTest {
// 回栏珊,注册一个额外的线程
public static final CyclicBarrier cyclicBarrier = new CyclicBarrier(4, () -> {
System.out.println("令牌已经为空了,准备唤醒等待中的线程.");
});
public static void main(String[] args) {
// 建立一个固定大小的线程池
ExecutorService executorService = Executors.newFixedThreadPool(4);
for (int i = 0; i < 4; i++) {
executorService.submit(() -> {
try {
String thName = Thread.currentThread().getName();
System.out.println(thName + " 执行完毕,等待通知...");
cyclicBarrier.await();
System.out.println(thName + " 收到通知.");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
// ignore
}
});
}
executorService.shutdown();
}
}
复制代码
执行结果以下:
Semaphore翻译过来是信号量。它的功能主要是控制并发线程数,特别适用于流量的控制,尤为是那些比较珍贵的公共资源。好比:数据库的链接,IO操做等。
咱们举个例子以下:
public class SemaphoreTest {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(1);
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "获取令牌成功");
Thread.sleep(100);
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
}
}
}
复制代码
Semaphore提供了acquire()方法来获取令牌,使用release来释放令牌。它也是基于AQS实现的。
本篇文章并无过多的去解读源码层面。截止到本篇文章,其实不难发现,若是掌握了Java并发的基本思想和底层基础知识后,对于应用层面的解读阅读大多数实际上是比较简单的。这也许就是知其因此然的好处罢!