1.先定义Callable对象,用于编写线程要执行代码(用call方法表示,此方法有返回值)
2.定义FutureTask对象封装Callable对象
3.定义Thread对象封装FutureTask对象, 并调用Thread对象的start()方法
具体代码如下:
package demo;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable t1=new MyCallable();
FutureTask task1=new FutureTask(t1);
Thread thread1=new Thread(task1,"T1");
thread1.start();
MyCallable t2=new MyCallable();
FutureTask task2=new FutureTask(t2);
Thread thread2=new Thread(task2,"T2");
thread2.start();
System.out.println(task1.get());
System.out.println(task2.get());
//注意:thread1和thread2最好分别执行两个task对象(两个task对象也分别采用两个Callable对象,否则只会有一个线程执行)
}
}
class MyCallable implements Callable<Integer>{
public Integer call() throws Exception {
int i=0;
for(;i<10000;i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
return i;
}
}
2.1.newFixedThreadPool线程池的概述
newFixedThreadPool是Java中的一个线程池类,它是一个固定大小的线程池,线程的数量在创建线程池时就已经确定。线程池中的线程数量一旦被确定,就不会发生改变。在Java中,newFixedThreadPool()方法创建的线程池是一个固定大小的线程池,线程池中的线程数量是固定的,由构造函数传入的参数指定,而任务队列的大小则由内部的阻塞队列来决定。
在使用newFixedThreadPool()方法创建线程池时,它使用的是LinkedBlockingQueue,这是一个无界的阻塞队列,它的大小是没有限制的。因此,当任务提交到线程池时,如果线程池中的线程正在执行任务,那么新提交的任务将被放入LinkedBlockingQueue中等待执行,直到有可用的线程来执行任务。当队列已经满了时,新提交的任务将会被阻塞,直到有空闲线程来处理队列中的任务。
因此,使用newFixedThreadPool()方法创建线程池时,队列的大小实际上是无限制的,但是需要注意的是,如果任务提交速度过快,队列可能会无限制地增长,导致内存溢出等问题。因此,在实际使用中需要根据具体的场景来合理设置线程池的大小和任务队列的容量,以充分利用系统资源并保证系统的稳定性。
2.2.newFixedThreadPool线程池的优缺点:
A.优点
B.newFixedThreadPool的缺点
2.3.线程池的两种主要任务提交方法:
在Java中,线程池的两种主要任务提交方法是execute()和submit()。它们虽然都是将任务提交到线程池中,但是在使用上有一些区别。
execute()方法没有返回值,而submit()方法会返回一个Future对象,可以通过这个对象获取任务的执行结果。
execute()方法中如果任务执行过程中发生了异常,则异常会被传递到任务提交的地方,并由任务提交的线程来处理。而submit()方法中,如果任务执行过程中发生了异常,异常将被封装在Future对象中,直到调用Future.get()方法时才会将异常抛出。
execute()方法只能接受Runnable类型的任务,而submit()方法既可以接受Runnable类型的任务,也可以接受Callable类型的任务。
execute()方法是一种异步提交方式,即提交任务后立即返回,不会等待任务执行完成。而submit()方法是一种同步提交方式,即提交任务后会阻塞当前线程,直到任务执行完成。
submit()方法返回的Future对象可以用来取消任务,而execute()方法没有提供取消任务的方法。
总之,execute()方法比submit()方法更简单,适用于不需要处理返回值的情况,而submit()方法则更为灵活,可以处理返回值,并且支持取消任务等操作。
用Callable创建的线程在线程池中运行,不需要FutureTask对象也不需要Thread对象
可以两个线程可以采用同一个Callable对像,也可以一个线程采用一个Callable对像
package demo;
import java.util.concurrent.*;
//用Callable创建的线程在线程池中运行
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable c1=new MyCallable();
//MyCallable c2=new MyCallable();
ExecutorService service= Executors.newFixedThreadPool(2);
//线程池的方式可以两个线程可以采用同一个Callable对像,也可以一个线程采用一个Callable对像
Future<Integer> f1=service.submit(c1);//不需要FutureTask对象也不需要Thread对象
Future<Integer> f2=service.submit(c1);
Integer rs1=f1.get();
System.out.println(rs1);
Integer rs2=f2.get();
System.out.println(rs2);
//service.shutdown();//关闭线程池
}
}
class MyCallable implements Callable<Integer>{
public Integer call() throws Exception {
int i=0;
for(;i<100;i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
return i;
}
}
用Runnable方式创建线程要执行的任务提交给newFixedThreadPool线程池来执行,代码如下:
package demo;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//使用newFixedThreadPool线程池
public class Test {
public static void main(String[] args) {
ExecutorService service= Executors.newFixedThreadPool(3);//创建
service.execute(new Task());//提交要执行的任务(此任务会由线程池的某一个线程来运行)
service.execute(new Task());//提交要执行的任务(此任务会由线程池的另一个线程来运行)
//service.shutdown();//关闭线程池
}
}
class Task implements Runnable{
public void run() {
for(int i=1;i<=5;i++){
System.out.println(Thread.currentThread().getName()+":run"+i);
}
}
}