并行计算
- 业务:任务多,数据量大
- 串行,并行
– 串行编程简单,并行编程困难
– 单个计算核频率下降,计算核数增多,整体性能变高(需要选择并行) - 并行困难(任务分配和执行过程的高度耦合)
– 如何分配粒度,切割任务
– 如何分配任务给线程,监督线程执行过程 - 并行模式
– 主从模式(Master-Slave)(主线程指挥副线程)
– Worker模式(Worker-Worker)(所有线程都是平等的) - Java并发编程
– Thread/Runnable/Thread组管理
– Executor框架
– Fork-Join框架
线程组管理
线程组ThreadGroup
- 线程的集合
- 树形结构,大线程组可以包括小线程组
- 可以通过
enumerate
方法遍历组内的线程,执行操作 - 能够有效管理多个线程,但是管理效率低
- 任务分配和执行过程的高度耦合 没有解决
- 重复创建线程,关闭线程操作,无法重用线程(线程new出来后,只能start一次,不能再次start)
activeCount
返回线程组中还处于active的线程数(估计数)
enumerate
将线程组中active的线程拷贝到数组中
interrupt
对线程组所有的线程发出interrupt信号
list
打印线程组中所有的线程信息
public class Main {
public static void main(String[] args) {
// 创建线程组
ThreadGroup threadGroup = new ThreadGroup("Searcher");
Result result=new Result();
// 创建一个任务,10个线程完成
Searcher searchTask=new Searcher(result);
for (int i=0; i<10; i++) {
Thread thread=new Thread(threadGroup, searchTask);
thread.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("========华丽丽0=======");
// 查看线程组消息
System.out.printf("active 线程数量: %d\n",threadGroup.activeCount());
System.out.printf("线程组信息明细\n");
threadGroup.list();
System.out.println("========华丽丽1=======");
// 遍历线程组
Thread[] threads=new Thread[threadGroup.activeCount()];
threadGroup.enumerate(threads);
for (int i=0; i<threadGroup.activeCount(); i++) {
System.out.printf("Thread %s: %s\n",threads[i].getName(),threads[i].getState());
}
System.out.println("========华丽丽2=======");
// Wait for the finalization of the Threadds
waitFinish(threadGroup); //等至少一个线程结束
// Interrupt all the Thread objects assigned to the ThreadGroup
threadGroup.interrupt();//向线程组里所有线程发送停止信号
}
public static void waitFinish(ThreadGroup threadGroup) {
while (threadGroup.activeCount()>9) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Searcher implements Runnable {
private Result result;
public Searcher(Result result) {
this.result=result;
}
@Override
public void run() {
String name=Thread.currentThread().getName();
System.out.printf("Thread %s: 启动\n",name);
try {
doTask();
result.setName(name);
} catch (InterruptedException e) {
System.out.printf("Thread %s: 被中断\n",name);
return;
}
System.out.printf("Thread %s: 完成\n",name);
}
private void doTask() throws InterruptedException { //睡眠随机时间
Random random=new Random((new Date()).getTime());
int value=(int)(random.nextDouble()*100);
System.out.printf("Thread %s: %d\n",Thread.currentThread().getName(),value);
TimeUnit.SECONDS.sleep(value);
}
}
Executor 框架
-
从JDK 5 开始提供Executor Frame Work(java.util.concurrent.)
– 分离任务的创建和执行者的创建
– 线程重复利用(new 线程的代价很大) -
理解共享线程池的概念
– 预设好多个Thread,可弹性增加
– 多次执行很多很小的任务
– 任务创建和执行过程解耦
– 程序员无需关心线程池执行任务过程 -
主要类:
–Executors.newCachedThreadPool/newFixedThreadPool
创建线程池
–ExecutorService
线程池服务
–Callable
具体的逻辑对象(线程类) 是接口 和Runnable
等价,里面的call
方法类似run
方法,但是是可以有返回值的
–Future
返回结果
public class Main {
public static void main(String[] args) throws InterruptedException {
// 创建一个执行服务器
Server server=new Server();
// 创建100个任务,并发给执行器,等待完成
for (int i=0; i<100; i++){
Task task=new Task("Task "+i); //自动来启动线程池里的线程数,进行计算任务
Thread.sleep(10);
server.submitTask(task); //给线程池任务
}
server.endServer(); //关闭线程池
}
}
/**
* 执行服务器
*
*/
public class Server {
//线程池
private ThreadPoolExecutor executor;
public Server(){
executor=(ThreadPoolExecutor)Executors.newCachedThreadPool(); //线程池容量自动增长
//executor=(ThreadPoolExecutor)Executors.newFixedThreadPool(5);
}
//向线程池提交任务
public void submitTask(Task task){
System.out.printf("Server: A new task has arrived\n");
executor.execute(task); //执行 无返回值
System.out.printf("Server: Pool Size: %d\n",executor.getPoolSize());
System.out.printf("Server: Active Count: %d\n",executor.getActiveCount());
System.out.printf("Server: Completed Tasks: %d\n",executor.getCompletedTaskCount());
}
public void endServer() {
executor.shutdown();
}
}
/**
* Task 任务类
* @author Tom
*
*/
public class Task implements Runnable {
private String name;
public Task(String name){
this.name=name;
}
public void run() {
try {
Long duration=(long)(Math.random()*1000);
System.out.printf("%s: Task %s: Doing a task during %d seconds\n",Thread.currentThread().getName(),name,duration);
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("%s: Task %s: Finished on: %s\n",Thread.currentThread().getName(),name,new Date());
}
}
计算总和的例子
public class SumTest {
public static void main(String[] args) {
// 执行线程池
ThreadPoolExecutor executor=(ThreadPoolExecutor)Executors.newFixedThreadPool(4);
List<Future<Integer>> resultList=new ArrayList<>();
//统计1-1000总和,分成10个任务计算,提交任务
for (int i=0; i<10; i++){
SumTask calculator=new SumTask(i*100+1, (i+1)*100);
Future<Integer> result=executor.submit(calculator);
resultList.add(result);
}
// 每隔50毫秒,轮询等待10个任务结束
do {
System.out.printf("Main: 已经完成多少个任务: %d\n",executor.getCompletedTaskCount());
for (int i=0; i<resultList.size(); i++) {
Future<Integer> result=resultList.get(i);
System.out.printf("Main: Task %d: %s\n",i,result.isDone()); //resul.isDone方法,如果这个线程已经被线程池计算出结果了,就true,不然false
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
} while (executor.getCompletedTaskCount()<resultList.size());
// 所有任务都已经结束了,综合计算结果
int total = 0;
for (int i=0; i<resultList.size(); i++) {
Future<Integer> result=resultList.get(i);
Integer sum=null;
try {
sum=result.get();
total = total + sum;
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
System.out.printf("1-1000的总和:" + total);
// 关闭线程池
executor.shutdown();
}
}
public class SumTask implements Callable<Integer> {
//定义每个线程计算的区间
private int startNumber;
private int endNumber;
public SumTask(int startNumber, int endNumber){
this.startNumber=startNumber;
this.endNumber=endNumber;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for(int i=startNumber; i<=endNumber; i++)
{
sum = sum + i;
}
Thread.sleep(new Random().nextInt(1000));
System.out.printf("%s: %d\n",Thread.currentThread().getName(),sum);
return sum;
}
}
其中我们发现,result中有result.isDone()方法,用来查看当前线程是否已经计算结束出结果,是返回true。还有多出了Callable类,类似Runnable类,也是接口。里面实现的是call方法,可以有返回值。而Runable类中run方法没有返回值。
Fork-Join框架
- Java 7 提供另一种并行框架:分解,治理,合并(分治编程)
- 适用于整体任务量不好确认的场合(最小任务可确定)
- 关键类
– ForkJoinPool 任务池
– RecursiveAction
– RecursiveTask
public class SumTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建执行线程池
ForkJoinPool pool = new ForkJoinPool();
//ForkJoinPool pool = new ForkJoinPool(4);
//创建任务
SumTask task = new SumTask(1, 10000000);
//提交任务
ForkJoinTask<Long> result = pool.submit(task);
//等待结果
do {
System.out.printf("Main: Thread Count: %d\n",pool.getActiveThreadCount());
System.out.printf("Main: Paralelism: %d\n",pool.getParallelism());
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
} while (!task.isDone()); //这里怎么是task.isDone 不是resul.isDone
//输出结果
System.out.println(result.get().toString());
}
}
//分任务求和
public class SumTask extends RecursiveTask<Long> {
private int start;
private int end;
public SumTask(int start, int end) {
this.start = start;
this.end = end;
}
public static final int threadhold = 5;
@Override
protected Long compute() {
Long sum = 0L;
// 如果任务足够小, 就直接执行
boolean canCompute = (end - start) <= threadhold;
if (canCompute) {
for (int i = start; i <= end; i++) {
sum = sum + i;
}
} else {
// 任务大于阈值, 分裂为2个任务
int middle = (start + end) / 2;
SumTask subTask1 = new SumTask(start, middle);
SumTask subTask2 = new SumTask(middle + 1, end);
invokeAll(subTask1, subTask2); //这俩子任务 提交了线程池,继续分解
Long sum1 = subTask1.join(); //等待俩子任务完成
Long sum2 = subTask2.join();
// 结果合并
sum = sum1 + sum2;
}
return sum;
}
}
这里Fork-Join框架,每次是提交一次任务。在这一次任务中,会分解成多个小任务,然后小任务继续分解,直到达到条件。然后结果在一层一层回来,类似于递归。
这里中,这里的任务,没有定义是线程类。???
然后result.isDone(),这里是task.isDone(),???