传统的多线程并没有提供高级特性,例如:信号量、线程池和执行管理器等,而这些特性恰恰有助于创建强大的并发程序。新的Fork/Join框架针对当前的多核系统,也提供了并行编程的可能。这块的内容是java多线程信息量最大的一部分内容,本篇博客循序渐进的,首先对concurrent包下的各模块常用类和接口进行实例展示,从使用中去了解concurrent包的内容。
java并发工具包处于java.util.concurrent包中,从上图中可以看出主要包括同步器、执行器、并发集合、Fork/Join框架、atomic包、locks包。
简单来说如下所示:
描述了一个在操作系统当中比较经典的信号量,主要作用是通过计数器来控制对共享资源的访问。
import cn.ji2h.util.LogUtil;
import java.util.concurrent.Semaphore;
public class SemaphoreDemo {
public static void main(String[] args){
//定义两个许可证
Semaphore semaphore = new Semaphore(2);
Persion p1 = new Persion(semaphore,"A");
p1.start();
Persion p2 = new Persion(semaphore,"B");
p2.start();
Persion p3 = new Persion(semaphore,"C");
p3.start();
}
}
class Persion extends Thread{
private Semaphore semaphore;
public Persion(Semaphore semaphore,String name){
setName(name);
this.semaphore = semaphore;
}
public void run(){
LogUtil.logger.info(getName() + " is waiting ......");
try {
semaphore.acquire();
LogUtil.logger.info(getName() + " is servicing ......");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
LogUtil.logger.info(getName() + " is done!");
semaphore.release();
}
}
模拟银行柜员服务的例子,三个客户,两个柜台。对应的三个线程,两个资源。
2018-10-02 16:28:41,229 [C] INFO cn.ji2h.util.LogUtil - C is waiting ......
2018-10-02 16:28:41,229 [A] INFO cn.ji2h.util.LogUtil - A is waiting ......
2018-10-02 16:28:41,229 [B] INFO cn.ji2h.util.LogUtil - B is waiting ......
2018-10-02 16:28:41,231 [A] INFO cn.ji2h.util.LogUtil - A is servicing ......
2018-10-02 16:28:41,231 [C] INFO cn.ji2h.util.LogUtil - C is servicing ......
2018-10-02 16:28:42,235 [C] INFO cn.ji2h.util.LogUtil - C is done!
2018-10-02 16:28:42,235 [A] INFO cn.ji2h.util.LogUtil - A is done!
2018-10-02 16:28:42,236 [B] INFO cn.ji2h.util.LogUtil - B is servicing ......
2018-10-02 16:28:43,240 [B] INFO cn.ji2h.util.LogUtil - B is done!
必须发生指定数量的时间后才可以继续执行
import cn.ji2h.util.LogUtil;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args){
//创建计数栓
CountDownLatch countDownLatch = new CountDownLatch(3);
//创建三个线程
new Racer(countDownLatch,"A").start();
new Racer(countDownLatch,"B").start();
new Racer(countDownLatch,"C").start();
//开始倒计时3...2...1...
for(int i = 0; i<3 ;i++){
try{
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
LogUtil.logger.info((3 - i)+ "");
if(i == 2){
LogUtil.logger.info("Start");
}
//出发执行起跑
countDownLatch.countDown();
}
}
}
class Racer extends Thread{
private CountDownLatch countDownLatch;
public Racer(CountDownLatch countDownLatch,String name){
setName(name);
this.countDownLatch = countDownLatch;
}
public void run(){
try{
countDownLatch.await();
for (int i = 0;i<3 ;i ++){
LogUtil.logger.info(getName() + " run : " + i);
}
} catch(InterruptedException e){
e.printStackTrace();
}
}
}
模拟倒计数3-2-1-start后,三个线程开始跑步的例子。
2018-10-02 16:37:23,316 [main] INFO cn.ji2h.util.LogUtil - 3
2018-10-02 16:37:24,323 [main] INFO cn.ji2h.util.LogUtil - 2
2018-10-02 16:37:25,324 [main] INFO cn.ji2h.util.LogUtil - 1
2018-10-02 16:37:25,324 [main] INFO cn.ji2h.util.LogUtil - Start
2018-10-02 16:37:25,324 [A] INFO cn.ji2h.util.LogUtil - A : 0
2018-10-02 16:37:25,324 [B] INFO cn.ji2h.util.LogUtil - B : 0
2018-10-02 16:37:25,325 [B] INFO cn.ji2h.util.LogUtil - B : 1
2018-10-02 16:37:25,325 [B] INFO cn.ji2h.util.LogUtil - B : 2
2018-10-02 16:37:25,324 [A] INFO cn.ji2h.util.LogUtil - A : 1
2018-10-02 16:37:25,324 [C] INFO cn.ji2h.util.LogUtil - C : 0
2018-10-02 16:37:25,325 [C] INFO cn.ji2h.util.LogUtil - C : 1
2018-10-02 16:37:25,325 [A] INFO cn.ji2h.util.LogUtil - A : 2
2018-10-02 16:37:25,325 [C] INFO cn.ji2h.util.LogUtil - C : 2
适合于只有多个线程都到达与定点时才可以继续执行
import cn.ji2h.util.LogUtil;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
public static void main(String[] args){
//定义一个循环屏障,这定义了三个
CyclicBarrier cyclicBarrier = new CyclicBarrier(3, new Runnable() {
public void run() {
LogUtil.logger.info("Game start!");
}
});
//创建三个工作线程
new Player(cyclicBarrier,"A").start();
new Player(cyclicBarrier,"B").start();
new Player(cyclicBarrier,"C").start();
}
}
class Player extends Thread{
private CyclicBarrier cyclicBarrier;
public Player(CyclicBarrier cyclicBarrier,String name){
setName(name);
this.cyclicBarrier = cyclicBarrier;
}
public void run(){
LogUtil.logger.info(getName() + " is waiting other players ...");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
模拟斗地主,三个人都准备好了再开始比赛的场景。每个线程在循环屏障处等待,主线程通过定义的循环屏障后继续执行。
简化两个线程间数据的交换
import cn.ji2h.util.LogUtil;
import java.util.concurrent.Exchanger;
public class ExchangerDemo {
public static void main(String[] args){
//定义一个交换器
Exchanger exchanger = new Exchanger();
//A或B谁先执行不确定
//若A先执行则A-B-B-A-A-B执行顺序
//若B先执行则B-A-A-B-B-A执行顺序
new A(exchanger).start();
new B(exchanger).start();
}
}
class A extends Thread{
private Exchanger exchanger;
public A(Exchanger exchanger){
this.exchanger = exchanger;
}
public void run(){
String str = null;
try {
str = exchanger.exchange("Hello");
LogUtil.logger.info(str);
str = exchanger.exchange("A");
LogUtil.logger.info(str);
str = exchanger.exchange("B");
LogUtil.logger.info(str);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class B extends Thread{
private Exchanger exchanger;
public B(Exchanger exchanger){
this.exchanger = exchanger;
}
public void run(){
String str = null;
try {
str = exchanger.exchange("Hi");
LogUtil.logger.info(str);
str = exchanger.exchange("1");
LogUtil.logger.info(str);
str = exchanger.exchange("2");
LogUtil.logger.info(str);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2018-10-02 17:09:26,396 [Thread-1] INFO cn.ji2h.util.LogUtil - Hello
2018-10-02 17:09:26,396 [Thread-0] INFO cn.ji2h.util.LogUtil - Hi
2018-10-02 17:09:26,398 [Thread-0] INFO cn.ji2h.util.LogUtil - 1
2018-10-02 17:09:26,398 [Thread-1] INFO cn.ji2h.util.LogUtil - A
2018-10-02 17:09:26,398 [Thread-1] INFO cn.ji2h.util.LogUtil - B
2018-10-02 17:09:26,398 [Thread-0] INFO cn.ji2h.util.LogUtil - 2
工作方式与CyclicBarrier类似,但是可以定义多个阶段
import cn.ji2h.util.LogUtil;
import java.util.concurrent.Phaser;
public class PhaserDemo {
public static void main(String[] args){
Phaser phaser = new Phaser(1);
LogUtil.logger.info("Starting ...");
//假设三个阶段
new Worker(phaser,"服务员").start();
new Worker(phaser,"厨师").start();
new Worker(phaser,"上菜").start();
for (int i = 1;i<= 3;i++){
//一共三个订单,对于每个订单,必须要求三个阶段都完成才可以进行下个订单
phaser.arriveAndAwaitAdvance();
LogUtil.logger.info("order " + i + " finished!");
}
phaser.arriveAndDeregister();
LogUtil.logger.info("all done!");
}
}
class Worker extends Thread{
private Phaser phaser;
public Worker(Phaser phaser,String name) {
this.setName(name);
this.phaser = phaser;
//将自己注册进来
phaser.register();
}
public void run(){
for (int i = 1;i<=3;i++){
LogUtil.logger.info(" current order is : " + i + ":" + getName());
if(i == 3){
//都处理完了注销自己
phaser.arriveAndDeregister();
}else {
//自己处理完了,等待其他的阶段处理
phaser.arriveAndAwaitAdvance();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2018-10-02 17:31:05,755 [main] INFO cn.ji2h.util.LogUtil - Starting ...
2018-10-02 17:31:05,757 [服务员] INFO cn.ji2h.util.LogUtil - current order is : 1:服务员
2018-10-02 17:31:05,758 [厨师] INFO cn.ji2h.util.LogUtil - current order is : 1:厨师
2018-10-02 17:31:05,758 [上菜] INFO cn.ji2h.util.LogUtil - current order is : 1:上菜
2018-10-02 17:31:05,758 [main] INFO cn.ji2h.util.LogUtil - order 1 finished!
2018-10-02 17:31:06,761 [上菜] INFO cn.ji2h.util.LogUtil - current order is : 2:上菜
2018-10-02 17:31:06,763 [厨师] INFO cn.ji2h.util.LogUtil - current order is : 2:厨师
2018-10-02 17:31:06,763 [服务员] INFO cn.ji2h.util.LogUtil - current order is : 2:服务员
2018-10-02 17:31:06,764 [main] INFO cn.ji2h.util.LogUtil - order 2 finished!
2018-10-02 17:31:07,768 [厨师] INFO cn.ji2h.util.LogUtil - current order is : 3:厨师
2018-10-02 17:31:07,768 [服务员] INFO cn.ji2h.util.LogUtil - current order is : 3:服务员
2018-10-02 17:31:07,768 [上菜] INFO cn.ji2h.util.LogUtil - current order is : 3:上菜
2018-10-02 17:31:07,768 [main] INFO cn.ji2h.util.LogUtil - order 3 finished!
2018-10-02 17:31:07,768 [main] INFO cn.ji2h.util.LogUtil - all done!
Callable:表示具有返回值的线程,
V:表示返回值类型
call():执行任务
Future:表示Callable的返回值,
V:返回值类型
get():获取返回值
import cn.ji2h.util.LogUtil;
import java.util.concurrent.*;
public class ExecutorDemo {
public static void main(String args[]){
//定义执行器,固定了2个线程的一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(2);
Future r1 = executorService.submit(new MC(1,100));
Future r2 = executorService.submit(new MC(100,10000));
try {
LogUtil.logger.info(r1.get() + " " + r2.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
executorService.shutdown();
}
}
class MC implements Callable {
private int begin,end;
public MC(int begin,int end){
this.begin = begin;
this.end = end;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i= begin; i
2018-10-02 17:38:58,803 [main] INFO cn.ji2h.util.LogUtil - 4950 49990050
java.util.concurrent.locks包中提供了对锁的支持。锁的作用是为使用synchronized控制对资源访问提供了替代机制。
mport cn.ji2h.util.LogUtil;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockDemo {
public static void main(String args[]){
new MT().start();
new MT().start();
new MT().start();
new MT().start();
}
}
class Data{
static int i = 0;
static Lock lock = new ReentrantLock();
static void operate(){
lock.lock();
i++;
LogUtil.logger.info(i + "");
lock.unlock();
}
}
class MT extends Thread{
public void run(){
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Data.operate();
}
}
}
2018-10-02 20:36:52,143 [Thread-0] INFO cn.ji2h.util.LogUtil - 1
2018-10-02 20:36:52,144 [Thread-1] INFO cn.ji2h.util.LogUtil - 2
2018-10-02 20:36:52,144 [Thread-2] INFO cn.ji2h.util.LogUtil - 3
2018-10-02 20:36:52,144 [Thread-3] INFO cn.ji2h.util.LogUtil - 4
2018-10-02 20:36:53,148 [Thread-0] INFO cn.ji2h.util.LogUtil - 5
2018-10-02 20:36:53,149 [Thread-1] INFO cn.ji2h.util.LogUtil - 6
2018-10-02 20:36:53,150 [Thread-2] INFO cn.ji2h.util.LogUtil - 7
2018-10-02 20:36:53,150 [Thread-3] INFO cn.ji2h.util.LogUtil - 8
2018-10-02 20:36:54,153 [Thread-0] INFO cn.ji2h.util.LogUtil - 9
2018-10-02 20:36:54,154 [Thread-1] INFO cn.ji2h.util.LogUtil - 10
2018-10-02 20:36:54,154 [Thread-2] INFO cn.ji2h.util.LogUtil - 11
2018-10-02 20:36:54,154 [Thread-3] INFO cn.ji2h.util.LogUtil - 12
这里的实例是4中的变种
java.util.concurrent.atom包中提供了对原子操作的支持,原子操作主要是提供了不需要锁以及其他同步机制就可以进行的一些不可中断操作。通过原子操作,能够简化对锁的一些控制。主要操作为:获取、设置、比较等。
import cn.ji2h.util.LogUtil;
import java.util.concurrent.atomic.AtomicInteger;
public class AtomDemo {
public static void main(String args[]){
new MT1().start();
new MT1().start();
new MT1().start();
new MT1().start();
}
}
class AtomData{
static int i = 0;
static AtomicInteger ai = new AtomicInteger(0);
static void operate(){
LogUtil.logger.info(ai.incrementAndGet() + "");
}
}
class MT1 extends Thread{
public void run(){
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
AtomData.operate();
}
}
}
2018-10-02 20:46:02,220 [Thread-3] INFO cn.ji2h.util.LogUtil - 1
2018-10-02 20:46:02,220 [Thread-1] INFO cn.ji2h.util.LogUtil - 4
2018-10-02 20:46:02,220 [Thread-0] INFO cn.ji2h.util.LogUtil - 2
2018-10-02 20:46:02,220 [Thread-2] INFO cn.ji2h.util.LogUtil - 3
2018-10-02 20:46:03,225 [Thread-1] INFO cn.ji2h.util.LogUtil - 7
2018-10-02 20:46:03,225 [Thread-2] INFO cn.ji2h.util.LogUtil - 8
2018-10-02 20:46:03,225 [Thread-0] INFO cn.ji2h.util.LogUtil - 5
2018-10-02 20:46:03,225 [Thread-3] INFO cn.ji2h.util.LogUtil - 6
2018-10-02 20:46:04,230 [Thread-2] INFO cn.ji2h.util.LogUtil - 10
2018-10-02 20:46:04,230 [Thread-3] INFO cn.ji2h.util.LogUtil - 12
2018-10-02 20:46:04,230 [Thread-0] INFO cn.ji2h.util.LogUtil - 11
2018-10-02 20:46:04,230 [Thread-1] INFO cn.ji2h.util.LogUtil - 9
线程执行顺序可忽视
这里我们使用Concurrent进行举例说明
ConcurrentHashMap 和 java.util.HashTable 类很相似,但 ConcurrentHashMap 能够提供比 HashTable 更好的并发性能。在你从中读取对象的时候 ConcurrentHashMap 并不会把整个 Map 锁住。此外,在你向其中写入对象的时候,ConcurrentHashMap 也不会锁住整个 Map。它的内部只是把 Map中正在被写入的部分进行锁定。
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ConcurrentHashMapDemo {
private static int INPUT_NUMBER = 100000;
public static void main(String[] args) throws InterruptedException {
// Map map = new Hashtable<>(12 * INPUT_NUMBER);
Map map = new ConcurrentHashMap<>(12 * INPUT_NUMBER);
long begin = System.currentTimeMillis();
ExecutorService service = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
service.execute(new InputWorker(map, i));
}
service.shutdown();
service.awaitTermination(1, TimeUnit.DAYS);
long end = System.currentTimeMillis();
System.out.println("span time = "+(end-begin)+", map size = "+map.size());
}
private static class InputWorker implements Runnable {
private static Random rand = new Random(System.currentTimeMillis());
private final Map map;
private final int flag;
private InputWorker(Map map, int begin) {
this.map = map;
this.flag = begin;
}
@Override
public void run() {
int input = 0;
while (input < INPUT_NUMBER) {
int x = rand.nextInt();
if (!map.containsKey(x)) {
map.put(x, "Alex Wang" + x);
input++;
}
}
System.out.println("InputWorker" + flag + " is over.");
}
}
}
InputWorker1 is over.
InputWorker3 is over.
InputWorker4 is over.
InputWorker8 is over.
InputWorker2 is over.
InputWorker0 is over.
InputWorker6 is over.
InputWorker9 is over.
InputWorker7 is over.
InputWorker5 is over.
span time = 324, map size = 1000000
这段代码中,生成10个子线程,每个线程向map中插入10万个键值对,然后计算耗时。ConcurrentHashMap平均耗时324ms,Hashtable平均耗时680ms,如此看来在我的普通PC机(i7,4核,16G内存)上,ConcurrentHashMap的耗时仅占Hashtable耗时的不到一半。
import cn.ji2h.util.LogUtil;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;
public class ForkJoinDemo {
public static void main(String[] args){
ForkJoinPool forkJoinPool = new ForkJoinPool();
Future result = forkJoinPool.submit(new MTask(0,1000001));
try {
LogUtil.logger.info(result.get() + "");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
forkJoinPool.shutdown();
}
}
class MTask extends RecursiveTask{
public static final int THRESHOLD = 1000;
private int begin,end;
public MTask(int begin, int end) {
this.begin = begin;
this.end = end;
}
@Override
protected Long compute() {
long sum = 0;
//判断任务是否满足条件1000个以内的操作
if((end - begin) <= THRESHOLD){
for(int i = begin;i
···
2018-10-02 21:30:01,689 [ForkJoinPool-1-worker-2] INFO cn.ji2h.util.LogUtil - 999026-1000001:974525175
2018-10-02 21:30:01,689 [ForkJoinPool-1-worker-2] INFO cn.ji2h.util.LogUtil - 998049-1000001:1949096799
2018-10-02 21:30:01,689 [ForkJoinPool-1-worker-2] INFO cn.ji2h.util.LogUtil - 996096-1000001:3894383295
2018-10-02 21:30:01,689 [ForkJoinPool-1-worker-2] INFO cn.ji2h.util.LogUtil - 992190-1000001:7773525378
2018-10-02 21:30:01,689 [ForkJoinPool-1-worker-2] INFO cn.ji2h.util.LogUtil - 984377-1000001:15487070286
2018-10-02 21:30:01,689 [ForkJoinPool-1-worker-2] INFO cn.ji2h.util.LogUtil - 968752-1000001:30730249947
2018-10-02 21:30:01,689 [ForkJoinPool-1-worker-2] INFO cn.ji2h.util.LogUtil - 937502-1000001:60484937394
2018-10-02 21:30:01,688 [ForkJoinPool-1-worker-0] INFO cn.ji2h.util.LogUtil - 843752-875001:26827999947
2018-10-02 21:30:01,689 [ForkJoinPool-1-worker-0] INFO cn.ji2h.util.LogUtil - 812502-875001:52680437394
2018-10-02 21:30:01,689 [ForkJoinPool-1-worker-0] INFO cn.ji2h.util.LogUtil - 750002-875001:101458624788
2018-10-02 21:30:01,689 [ForkJoinPool-1-worker-0] INFO cn.ji2h.util.LogUtil - 875002-1000001:117067624788
2018-10-02 21:30:01,689 [ForkJoinPool-1-worker-3] INFO cn.ji2h.util.LogUtil - 750002-1000001:218526249576
2018-10-02 21:30:01,689 [ForkJoinPool-1-worker-1] INFO cn.ji2h.util.LogUtil - 500001-1000001:374616999162
2018-10-02 21:30:01,694 [main] INFO cn.ji2h.util.LogUtil - 499488998835
极客学院:https://www.jikexueyuan.com/course/java/