1、Fork-Join
Fork/Join框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不可拆发),再将一个个的小任务运算的结果进行join汇总。MapReduce就是这种思想。
Fork/Join的标准使用范式:
实现案例:
public class MakeArray {
public static final int ARRAY_LENGTH = 1000000;
public static int[] makeArray() {
Random random = new Random();
int[] ints = new int[ARRAY_LENGTH];
for (int i = 0; i < ARRAY_LENGTH; i++) {
ints[i] = random.nextInt(ARRAY_LENGTH * 3);
}
return ints;
}
}
public class SumArray {
private static class SumTask extends RecursiveTask<Integer> { //继承抽象类
private final static int THRESHOLD = MakeArray.ARRAY_LENGTH/10;
private int[] src; //表示要实际统计的数组
private int fromIndex;//开始统计的下标
private int toIndex;//统计到哪里结束的下标
public SumTask(int[] src, int fromIndex, int toIndex) {
this.src = src;
this.fromIndex = fromIndex;
this.toIndex = toIndex;
}
@Override
protected Integer compute() {
if (toIndex - fromIndex < THRESHOLD) {
int count = 0;
for (int i = fromIndex; i < toIndex; i++) {
count = count + src[i];
}
return count;
} else {
int mid = (fromIndex + toIndex) >> 1;
SumTask left = new SumTask(src, fromIndex, mid); //拆分
SumTask right = new SumTask(src, mid, toIndex);
invokeAll(left, right);
return left.join() + right.join();
}
}
}
public static void main(String[] args) {
ForkJoinPool pool = new ForkJoinPool();
int[] src = MakeArray.makeArray();
long start = System.currentTimeMillis();
SumTask sumTask = new SumTask(src, 0, src.length);
pool.invoke(sumTask); //同步调用
System.out.println("Task is running...");
System.out.println("The count is " + sumTask.join() + ", spend time: " +
(System.currentTimeMillis() - start) + "ms");
}
}
2、CountDownLatch
作用:是一组线程等待其他的线程完成工作以后在执行,加强版join
await()用来等待,countDown()负责计数器减一
/**
* 类说明:演示CountDownLatch,有5个初始化线程,6个扣除点
* 扣除完毕后,主线程和业务线程才能继续自己的工作
*/
public class UseCountDownLatch {
static CountDownLatch latch = new CountDownLatch(6);
//初始化线程(只有一步, 有4个线程)
private static class InitThread implements Runnable {
@Override
public void run() {
System.out.println("Thread_" + Thread.currentThread().getId() + " ready init work...");
latch.countDown();//初始化线程完成工作了,countDown方法只扣减一次
for (int i = 0; i < 2; i++) {
System.out.println("Thread_" + Thread.currentThread().getId() + ".....continue to do it's work");
}
}
}
//业务线程
private static class BusyThread implements Runnable {
@Override
public void run() {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("BusyThread_" + Thread.currentThread().getId() + " do business----");
}
}
public static void main(String[] args) throws InterruptedException {
//单独的初始化线程,初始化分为2步,需要扣减2次
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1);
System.out.println("Thread_" + Thread.currentThread().getId() + " ready init work step 1st....");
latch.countDown();//每完成一步初始化工作,扣减一次
System.out.println("begin step 2nd...");
Thread.sleep(1);
System.out.println("Thread_"+Thread.currentThread().getId()
+" ready init work step 2nd......");
latch.countDown();//每完成一步初始化工作,扣减一次
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new BusyThread()).start();
for (int i = 0; i <= 3; i++) {
Thread thread = new Thread(new InitThread());
thread.start();
}
latch.await();
System.out.println("Main do it's work...");
}
}
输出结果:
Thread_10 ready init work step 1st....
begin step 2nd...
Thread_10 ready init work step 2nd......
Thread_12 ready init work...
Thread_12.....continue to do it's work
Thread_12.....continue to do it's work
Thread_13 ready init work...
Thread_13.....continue to do it's work
Thread_13.....continue to do it's work
Thread_14 ready init work...
Thread_14.....continue to do it's work
Thread_14.....continue to do it's work
Thread_15 ready init work...
Thread_15.....continue to do it's work
Thread_15.....continue to do it's work
BusyThread_11 do business----
Main do it's work...
3、CyclicBarrier
作用:让一组线程达到某个屏障被阻塞,一直到组内最后一个线程达到屏障时,屏障开放,所有被阻塞的线程会继续运行CyclicBarrier(int parties)
CyclicBarrier(int parties, Runnable barrierAction),屏障开放,barrierAction定义的任务会执行
public class UseCyclicBarrier {
private static CyclicBarrier barrier = new CyclicBarrier(5,new CollectThread());
private static ConcurrentHashMap<String,Long> resultMap
= new ConcurrentHashMap<>();//存放子线程工作结果的容器
//负责屏障开放以后的工作
private static class CollectThread implements Runnable {
@Override
public void run() {
StringBuilder builder = new StringBuilder();
for (Map.Entry<String, Long> entry : resultMap.entrySet()) {
builder.append("[" + entry.getValue() + "]");
}
System.out.println("result = " + builder);
System.out.println("do other business...");
}
}
//工作线程
private static class SubThread implements Runnable {
@Override
public void run() {
long id = Thread.currentThread().getId();//
resultMap.put(Thread.currentThread().getId() + "", id);
Random random = new Random();//随机决定工作线程是否睡眠
try {
if (random.nextBoolean()) {
Thread.sleep(2000 + id);
System.out.println("Thread_" + id + "...do something");
}
System.out.println(id + "...is await");
barrier.await();
Thread.sleep(2000 + id);
System.out.println("Thread_" + id + "...do it's business");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
for(int i=0;i<=4;i++){
Thread thread = new Thread(new SubThread());
thread.start();
}
}
}
输出结果:
10...is await
11...is await
12...is await
Thread_14...do something
14...is await
Thread_13...do something
13...is await
result = [11][12][13][14][10]
do other business...
Thread_11...do it's business
Thread_10...do it's business
Thread_12...do it's business
Thread_13...do it's business
Thread_14...do it's business
4、CountDownLatch与CyclicBarrier的区别
- CountDownLatch放行由第三者控制,CyclicBarrier放行由一组线程本身控制
- CountDownLatch放行条件 >= 线程数,CyclicBarrier放行条件 = 线程数
5、Semaphore
控制同时访问某个特定资源的线程数量,一般用在流量控制
public class DBPoolSemaphore {
private final static int POOL_SIZE = 10;
private final Semaphore useful, useless;//useful表示可用的数据库连接,useless表示已用的数据库连接
public DBPoolSemaphore() {
this.useful = new Semaphore(POOL_SIZE);
this.useless = new Semaphore(0);
}
//存放数据库连接的容器
private static LinkedList<Connection> pool = new LinkedList<>();
//初始化池
static {
for (int i = 0; i < POOL_SIZE; i++) {
pool.addLast(SqlConnectImpl.fetchConnection());
}
}
//归还连接
public void returnConnection(Connection connection) throws InterruptedException {
if (connection != null) {
System.out.println("当前有" + useful.getQueueLength() + "个线程等待数据库连接!!" +
"可用连接数:" + useful.availablePermits());
useless.acquire();
synchronized (pool) {
pool.addLast(connection);
}
useful.release();
}
}
//从连接池中获取连接
public Connection getConnection() throws InterruptedException {
useful.acquire();
Connection conn;
synchronized (pool) {
conn = pool.removeFirst();
}
useless.release();
return conn;
}
private static class SqlConnectImpl implements Connection {
public static final Connection fetchConnection(){
return new SqlConnectImpl();
}
//省略其它代码
}
}
public class SemaphoreClient {
private static DBPoolSemaphore dbPool = new DBPoolSemaphore();
//业务线程
private static class BusyThread extends Thread {
@Override
public void run() {
Random random = new Random();//让每一个线程持有连接的时间不一样
long start = System.currentTimeMillis();
try {
Connection connection = dbPool.getConnection();
System.out.println("Thread_" + Thread.currentThread().getId() + "_获取数据库连接共耗时【" +
(System.currentTimeMillis() - start) + "】ms");
Thread.sleep(100 + random.nextInt(100));//模拟业务线程,线程持有连接使用时间
System.out.println("查询数据完成,归还连接!");
dbPool.returnConnection(connection);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
for (int i = 0; i < 50; i++) {
new BusyThread().start();
}
}
}
6、Exchange
两个线程之间的数据交换,实际应用中会比较少用
7、Callable、Future和FutureTask
isDone():结束;正常还是异常结束,或者自己取消,返回true
isCancelled():任务完成前被取消,返回true
cancel(boolean):
- 任务还没开始,返回true
- 任务已经开启,cancel(true),中断正在运行的任务,中断成功,返回true;cancel(false)不会去中断正在运行中的任务
- 任务已经结束,返回false
public class FutureClient {
//实现Callable接口,允许有返回值
private static class UseCallable implements Callable<Integer> {
private int sum;
@Override
public Integer call() throws Exception {
System.out.println("Callable子线程开始计算");
Thread.sleep(2000);
for (int i = 0; i < 5000; i++) {
sum = sum + i;
}
System.out.println("Callable子线程计算完成,结果 = " + sum);
return sum;
}
}
public static void main(String[] args) throws Exception {
UseCallable call = new UseCallable();
FutureTask<Integer> futureTask = new FutureTask<>(call);
new Thread(futureTask).start();
Random random = new Random();
Thread.sleep(1);
if (random.nextBoolean()) {
System.out.println("Get UseCallable result = " + futureTask.get());
} else {
System.out.println("中断计算");
futureTask.cancel(true);
}
}
}