在java中,多线程的创建方式总共有4种,其中继承Thread类与实现Runnable接口这两种方法最简单,但是实际开发过程中用的比较多的是另外两种方式:一种是实现Callable接口的方式来创建多线程,一种是利用线程池创建多线程。后两者相较前两者而言,虽然实现方式上复杂了些,但是都能够定义返回值,同时能够抛出异常。以下是四种方式创建多线程的java代码实例:
1.继承Thread类
public class Test1 extends Thread{
int sum=0;
public void run(){
System.out.println("执行run方法");
for(int i=0;i<50;i++){
sum+=i;
System.out.println(Thread.currentThread().getName()+" sum:"+sum);
}
}
}
2.实现Runnable接口
public class Test2 implements Runnable{
int sum=0;
public void run(){
System.out.println("执行run方法");
for(int i=0;i<50;i++){
sum+=i;
System.out.println(Thread.currentThread().getName()+"sum:"+sum);
}
}
}
3.实现Callable接口,Callable同Runnable不一样的地方在于,需要实现call方法,方法的返回值类型即为实现Callable时的泛型。如下例所示,Integer即为实现的泛型,同时call方法的返回值也必须是Integer。获取返回值需要通过FutureTask类,测试类有示例代码。
public class Test3 implements Callable<Integer>{
public Integer call() throws Exception {
int sum=0;
for(int i=0;i<30;i++){
sum+=i;
System.out.println(Thread.currentThread().getName()+" sum:"+sum);
}
return sum;
}
}
4.线程池方式
以前常用的创建线程池的方式是:
Executor threadPool = Executors.newFixedThreadPool(10);
for(int i = 0 ;i < 10 ; i++) {
threadPool.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName()+" is running");
}
});
}
但是阿里java技术规范建议我们不要这样做,因为Executors提供的创建线程的方式都有不同程度的OOM风险。
线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
因此我们采用手动创建线程池的方式。
首先引入com.google.guava包
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("demo-pool-%d").build();
//Common Thread Pool
ExecutorService pool = new ThreadPoolExecutor(5, 200,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
pool.execute(()-> System.out.println(Thread.currentThread().getName()));
pool.shutdown();//gracefully shutdown
测试类:
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
*@date 2017-5-25
*@description 多线程的三种实现方式测试
**/
public class MainTest {
/**
* 2017-5-25
*/
public static void main(String[] args) {
//继承thread类
Test1 test1_1 = new Test1();
Test1 test1_2 = new Test1();
test1_1.start();
test1_2.start();
//实现runnable接口
Test2 test2_1 = new Test2();
Test2 test2_2 = new Test2();
Thread a = new Thread(test2_1);
Thread b = new Thread(test2_2);
a.start();
b.start();
//通过实现Callable接口
Test3 test3_1 = new Test3();
FutureTask<Integer> ft = new FutureTask<Integer>(test3_1);
Thread c = new Thread(ft);
Test3 test3_2 = new Test3();
FutureTask<Integer> ft2 = new FutureTask<Integer>(test3_2);
Thread d = new Thread(ft2);
c.start();
d.start();
try {
System.out.println(ft.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
扩展:在创建使用多线程的方式3中,我们使用了FutureTask类的方式获取返回值。不过,使用FutureTask类会有一个弊端,那就是如果任务执行时间较长,而我们又需要拿到返回值才能执行后续的操作的话,只能不断的调用FutureTask的get()方法阻塞式的获取值。因此在1.8之前的实际开发中,我们更倾向于使用另一种方式:ExecutorCompletionService。JDK1.8的包中新增了CompletableFuture 和 CompletionStage两个类,这两个类是对Future缺陷的补充。CompletableFuture实现了Future和CompletionStage两个接口。