Java多线程的实现方式有:继承Thread类、实现Runnable接口、实现Callable接口通过FutureTask包装器来创建Thread线程、使用ExecutorService和Callable以及Future实现有返回结果的多线程。
1、继承Thread类
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println("线程:" + Thread.currentThread().getName() + "第" + (i+1) + "次执行");
}
}
}
====
public class MyThreadTest {
public static void main(String[] args) throws Exception{
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start();
t2.start();
}
}
===测试结果
线程:Thread-0第1次执行
线程:Thread-0第2次执行
线程:Thread-1第1次执行
线程:Thread-0第3次执行
线程:Thread-0第4次执行
...
测试结果因机器、系统等原因可能会不同。
- 结论:
- 直接调用对象.run()方法并不会启动线程,这相当于普通的方法执行。
2、实现Runnable接口
public class ThreadRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println("线程:" + Thread.currentThread().getName() + "第" + (i+1) + "次执行");
}
}
}
====
public class ThreadRunnableTest {
public static void main(String[] args) {
Thread t1 = new Thread(new ThreadRunnable());
new Thread(() -> {
for (int i = 0; i < 10000; i++) {
System.out.println("=====我想打游戏=====");
}
}).start();
t1.start();
}
}
===测试结果
=====我想打游戏=====
=====我想打游戏=====
=====我想打游戏=====
线程:Thread-0第1次执行
线程:Thread-0第2次执行
线程:Thread-0第3次执行
...
- 结论
- 结合函数式编程,可以基于lambda表达式(匿名内部类形式)实现Runnable接口来实现多线程。
- java类继承为单继承,当一个类已经继承其他类的情况下,想要实现多线程,可以通过实现Runnable接口的方式,来实现多线程。
3、实现Callable接口
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> task = new FutureTask<>(() -> "~~~~我在敲代码~~~~");
new Thread(task).start();
String s = task.get();
System.out.println(s);
}
====测试结果
~~~~我在敲代码~~~~
- 结论
- 执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回值了。get方法是阻塞的,即:线程无返回结果,get方法会一直等待。
- FutureTask实现了Runnable和Future接口。
- 前面2种方式实现主线程,方法没有返回值,不能抛异常;
4、线程池的方式
ExecutorService接口和Callable接口以及 Future接口都是属于Executor框架。
Executor框架是在Java1.5中引入的,它把任务的提交和执行进行解耦,只需要将写好的任务方法交给线程池就行了。
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
Date start = new Date();
Thread.sleep(1000);
Date end = new Date();
return Thread.currentThread().getName()+"执行用时:"+(end.getTime() - start.getTime()) + "毫秒";
}
}
====
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// FutureTask<String> task = new FutureTask<>(() -> "~~~~我在敲代码~~~~");
// new Thread(task).start();
// String s = task.get();
// System.out.println(s);
Date start = new Date();
// 创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
List<Future<String>> ret = new ArrayList<>(5);
for (int i = 0; i < 5; i++) {
Callable<String> call = new MyCallable();
// 提交任务
Future<String> future = executorService.submit(call);
ret.add(future);
}
// 关闭线程池
executorService.shutdown();
// 获取返回结果
for (Future<String> f: ret) {
System.out.println(f.get());
}
Date end = new Date();
System.out.println(Thread.currentThread().getName()+"程序结束,执行用时:"+(end.getTime() - start.getTime()) + "毫秒");
}
}
====测试结果
pool-1-thread-1执行用时:1000毫秒
pool-1-thread-2执行用时:1000毫秒
pool-1-thread-3执行用时:1000毫秒
pool-1-thread-4执行用时:1000毫秒
pool-1-thread-5执行用时:1000毫秒
main程序结束,执行用时:1004毫秒
再介绍Executors类:提供了一系列工厂方法用于创建线程池,返回的线程池都实现了ExecutorService接口。
- public static ExecutorService newFixedThreadPool(int nThreads)
创建固定数目线程的线程池。 - public static ExecutorService newCachedThreadPool()
创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。 - public static ExecutorService newSingleThreadExecutor()
创建一个单线程化的Executor。 - public static ScheduledExecutorService newScheduledThreadPool(int
corePoolSize)
创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。 - ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。
参考地址:Java多线程实现的四种方式
欢迎交流,本人 QQ: 806797785。
源代码地址:https://gitee.com/gaogzhen/concurrent.git