并行模式
-主从模式(Master-Slave)
-Worker模式(Worker-Worker)
·Java并发编程
-Thread/Runnable/Thread组管理
-Executor(本节重点)
-Fork-Join框架
线程组ThreadGroup
-线程的集合
-树形结构,大线程组可以包括小线程组
-可以通过enumerate方法遍历组内的线程,执行操作
-能够有效管理多个线程,但是管理效率低
-任务分配和执行过程高度耦合
-重复创建线程、关闭线程操作,无法重用线程
package thread.threadgroup;
import java.util.concurrent.TimeUnit;
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("++++++++++++++++++++++++++++++++++++");
System.out.printf("active 线程数量:%d\n",threadGroup.activeCount());
System.out.printf("线程组信息明细\n");
threadGroup.list();
System.out.println("===========================");
//遍历线程组
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("===========================");
waitFinish(threadGroup);
threadGroup.interrupt();
}
public static void waitFinish(ThreadGroup threadGroup){
while (threadGroup.activeCount()>9){
try {
TimeUnit.SECONDS.sleep(1);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
package thread.threadgroup;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.TimeUnit;
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);
}
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);
}
}
package thread.threadgroup;
public class Result {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
activeCount,返回线程组中还处于active的线程数(估计数);enumerate,将线程组中active的线程拷贝到数组中;interrupt,对线程组中所有的线程发出interrupt信号;打印线程组中所有的线程信息
Executor框架
从JDK5开始提供Executor FrameWork (java.util.concurrent.*)
-分离任务的创建和执行者的创建
-线程重复利用(new线程代价很大)理解共享线程池的概念
-预设好的多个Thread,可弹性增加
多次执行很多很小的任务
任务创建和执行过程解耦一
程序员无需关心线程池执行任务过程
主要类:ExecutorService,ThreadPoolExecutor,Future-Executors.newCachedThreadPool/newFixedThreadPool 创建线程池
-ExecutorService 线程池服务
-Callable 具体的逻辑对象(线程类)
-Future 返回结果
public class Main {
public static void main(String[] args) throws InterruptedException{
Server server = new Server();
for (int i=0;i<100;i++){
Task task = new Task("Task "+i);
Thread.sleep(10);
server.submitTask(task);
}
server.endServer();
}
}
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
public class Server {
private ThreadPoolExecutor executor;
public Server(){
executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
//executor = (ThreadPoolExecutor) Executor.newCachedThreadPool();
}
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 Account: %d\n", executor.getActiveCount());
System.out.printf("Server: Completed Tasks: %d\n", executor.getCompletedTaskCount());
}
public void endServer(){
executor.shutdown();
}
}
package org.example.executor;
import java.util.Date;
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(),Thread.currentThread().getId(),duration);
Thread.sleep(duration);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.printf("%s: Task %s: Finished on: %s\n", Thread.currentThread().getName(),Thread.currentThread().getId(),new Date().getTime());
}
}
另一个例子
package org.example.executor;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
public class SumTest {
public static void main(String[] args){
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(4);
List<Future<Integer>> resultList =new ArrayList<>();
for (int i = 0; i <10; i++) {
SumTask calcuator = new SumTask(i*100+1,(i+1)*100);
Future<Integer> result = executor.submit(calcuator);
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());
}
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();
}
}
package org.example.executor;
import java.util.Random;
import java.util.concurrent.Callable;
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;
}
}
在Java中,Future
接口是java.util.concurrent包的一部分,它用于表示异步计算的结果。一个 Future
对象用于封装那些可能还没有完成的计算任务的结果。使用 Future
,你可以启动一个计算任务,然后立即继续执行其他任务,而不是等待计算完成。当需要计算结果时,你可以通过 Future
对象来获取它,如果计算尚未完成,那么 Future
的 get
方法会阻塞当前线程直到计算完成。
Future 的主要方法
V get() throws InterruptedException, ExecutionException
:等待计算完成,然后检索其结果。如果计算尚未完成,则此方法会阻塞当前线程直到它完成。V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
:等待计算完成,直到给定的等待时间结束,然后检索其结果。boolean isDone()
:如果计算完成,则返回true
。boolean isCancelled()
:如果计算被取消,则返回true
。boolean cancel(boolean mayInterruptIfRunning)
:尝试取消计算。如果计算正在运行,则根据mayInterruptIfRunning
参数的值,可能会尝试停止计算。
在Java的java.util.concurrent
包中,Callable
接口与Runnable
接口相似,但有一个关键的区别:Callable
接口的call
方法可以返回一个结果,并且可以抛出一个异常,而Runnable
接口的run
方法既没有返回值也不能抛出受检查的异常(只能抛出RuntimeException
或Error
)。
Fork-Join框架
提供另一种并行框架:分解、治理、合并(分治编程)
适合用于整体任务量不好确定的场合(最小任务可确定)
package org.example.executor1;
import java.util.concurrent.*;
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());
System.out.println(result.get().toString());
}
}
package org.example.executor1;
import java.util.concurrent.RecursiveTask;
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
public Long compute() {
Long sum = 0L;
boolean canCompute = (end - start) <= threadhold;
if (canCompute) {
for (int i = start; i <= end; i++) {
sum = sum + i;
}
} else {
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;
}
}