[超级链接:Java并发学习系列-绪论]
本章主要对Java多线程实现的三种方式进行学习。
在JDK5版本之前,提供了两种多线程的实现方式:
这两种种方式的本质都是一个:实现Runnable接口。
在JDK5版本时,提供了一种新的多线程实现方式:
下面分别对这三种实现方式进行学习。
我们先来看以下Runnable接口的定义:
package java.lang;
/**
* The Runnable
interface should be implemented by any
* class whose instances are intended to be executed by a thread. The
* class must define a method of no arguments called run
.
*
* This interface is designed to provide a common protocol for objects that
* wish to execute code while they are active. For example,
* Runnable
is implemented by class Thread
.
* Being active simply means that a thread has been started and has not
* yet been stopped.
*
* ...
*/
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface Runnable
is used
* to create a thread, starting the thread causes the object's
* run
method to be called in that separately executing
* thread.
*
*/
public abstract void run();
}
总结:
实现Runnable实现线程的语法:
//定义
public class MyRunnableImpl implements Runnable {
@Override
public void run(){
//..
}
}
//使用
new Thread(new MyRunnableImpl()).start();
说明:
实例场景:
创建5个线程,每个线程随机执行一段时间后结束。
实例代码:
/**
* 自定义线程02:实现Runnable接口
* @author hanchao 2018/3/8 23:04
**/
public class MyRunnableImpl implements Runnable{
private static final Logger LOGGER = Logger.getLogger(MyRunnableImpl.class);
/** 线程名(需要手动指定) */
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public MyRunnableImpl(String name) {
this.name = name;
}
/**
* 义务代码在run()方法中,此方法无返回值
* @author hanchao 2018/3/8 23:07
**/
@Override
public void run() {
Integer interval = RandomUtils.nextInt(1000,5000);
LOGGER.info("线程[" + this.getName() + "]正在运行,预计运行" + interval + "...");
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
LOGGER.info("...线程[" + this.getName() + "]运行结束");
}
}
/**
* 自定义线程实现类测试
* @author hanchao 2018/3/8 23:07
**/
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
//通过new创建一个线程
Runnable runnable = new MyRunnableImpl("MyRunnableImpl-" + i);
//通过new Thread.start()启动线程
new Thread(runnable).start();
}
}
}
运行结果:
2018-03-12 10:11:19 INFO MyRunnableImpl:40 - 线程[MyRunnableImpl-0]正在运行,预计运行1442...
2018-03-12 10:11:19 INFO MyRunnableImpl:40 - 线程[MyRunnableImpl-1]正在运行,预计运行1250...
2018-03-12 10:11:19 INFO MyRunnableImpl:40 - 线程[MyRunnableImpl-2]正在运行,预计运行3603...
2018-03-12 10:11:19 INFO MyRunnableImpl:40 - 线程[MyRunnableImpl-3]正在运行,预计运行3290...
2018-03-12 10:11:19 INFO MyRunnableImpl:40 - 线程[MyRunnableImpl-4]正在运行,预计运行3758...
2018-03-12 10:11:20 INFO MyRunnableImpl:46 - ...线程[MyRunnableImpl-1]运行结束
2018-03-12 10:11:21 INFO MyRunnableImpl:46 - ...线程[MyRunnableImpl-0]运行结束
2018-03-12 10:11:22 INFO MyRunnableImpl:46 - ...线程[MyRunnableImpl-3]运行结束
2018-03-12 10:11:23 INFO MyRunnableImpl:46 - ...线程[MyRunnableImpl-2]运行结束
2018-03-12 10:11:23 INFO MyRunnableImpl:46 - ...线程[MyRunnableImpl-4]运行结束
先来看Thread类的定义:
/**
* A thread is a thread of execution in a program. The Java
* Virtual Machine allows an application to have multiple threads of
* execution running concurrently.
*
* ...
*/
public
class Thread implements Runnable {
//...
/**
* If this thread was constructed using a separate
* Runnable
run object, then that
* Runnable
object's run
method is called;
* otherwise, this method does nothing and returns.
*
* ...
*/
@Override
public void run() {
if (target != null) {
target.run();
}
}
}
说明:
语法:
//定义
public class MyThread extends Thread {
@Override
public void run(){
//..
}
}
//使用
new MyThread().start();
说明:
super
来调用。实例场景:
创建5个线程,每个线程随机执行一段时间后结束。
实例代码:
/**
* 自定义线程01:继承自Thread
* @author hanchao 2018/3/8 22:54
**/
public class MyThread extends Thread {
private static final Logger LOGGER = Logger.getLogger(MyThread.class);
/**
* 重写Thread类的构造器,用以给线程命名
* 此种方式无需定义name变量以指定线程名,因为父类Thread中已有。
* @author hanchao 2018/3/8 22:59
**/
public MyThread(String name) {
super(name);
}
/**
* 业务代码写在run()方法中,此方法无返回值
* @author hanchao 2018/3/8 22:55
**/
@Override
public void run(){
//run()方法无法抛出异常
// public void run() throws Exception{
Integer interval = RandomUtils.nextInt(1000,9000);
LOGGER.info("线程[" + super.getName() + "]正在运行,预计运行" + interval + "...");
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
LOGGER.info("...线程[" + super.getName() + "]运行结束");
}
}
/**
* 测试自定义线程
* @author hanchao 2018/3/8 22:57
**/
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
//通过new创建一个线程
Thread thread = new MyThread("MyThread-" + i);
//通过start()启动线程
thread.start();
}
}
}
运行结果:
2018-03-12 10:18:35 INFO MyThread:31 - 线程[MyThread-3]正在运行,预计运行6561...
2018-03-12 10:18:35 INFO MyThread:31 - 线程[MyThread-4]正在运行,预计运行7464...
2018-03-12 10:18:35 INFO MyThread:31 - 线程[MyThread-1]正在运行,预计运行4806...
2018-03-12 10:18:35 INFO MyThread:31 - 线程[MyThread-2]正在运行,预计运行5214...
2018-03-12 10:18:35 INFO MyThread:31 - 线程[MyThread-0]正在运行,预计运行8557...
2018-03-12 10:18:40 INFO MyThread:37 - ...线程[MyThread-1]运行结束
2018-03-12 10:18:40 INFO MyThread:37 - ...线程[MyThread-2]运行结束
2018-03-12 10:18:42 INFO MyThread:37 - ...线程[MyThread-3]运行结束
2018-03-12 10:18:43 INFO MyThread:37 - ...线程[MyThread-4]运行结束
2018-03-12 10:18:44 INFO MyThread:37 - ...线程[MyThread-0]运行结束
先来分析之前两种实现方式的弊端。
通过分析Runnable接口的定义,很容易总结出来:
为了解决以上的问题,在JDK5版本的java.util.concurretn包中,引入了新的线程实现机制:Callable接口。
我们先来看一下Callable接口的语法:
package java.util.concurrent;
/**
* A task that returns a result and may throw an exception.
* Implementors define a single method with no arguments called
* {@code call}.
*
* The {@code Callable} interface is similar to {@link
* java.lang.Runnable}, in that both are designed for classes whose
* instances are potentially executed by another thread. A
* {@code Runnable}, however, does not return a result and cannot
* throw a checked exception.
* ...
*/
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
总结:
实现Callable接口的语法:
//定义
public class MyCallableImpl implements Callable<T> {
@Override
public T call() throws Exception {
//...
}
}
//使用
//一般配置Executor使用,Executor提供线程池服务
ExecutorService executor = new ....
//一般配置Future接口使用,Future用于保存返回结果
//向线程池提交一个任务,并把此任务的执行情况保存在future中
Futrue future = executor.submit(new MyCallableImple());
//获取返回结果
future.get();
//关闭线程池和任务
executor.shutdwon();
说明:
实例场景:
定义一个最大线程数为5的线程池,运行5个任务,获取并打印出每个线程的执行时间。
实例代码:
/**
* 自定义线程03:实现Callable接口
*
* @author hanchao 2018/3/12 8:56
**/
//注意,Callable是一个泛型接口
public class MyCallableImpl implements Callable<Integer> {
private static final Logger LOGGER = Logger.getLogger(MyCallableImpl.class);
/**
* 实现Callable需要重写call方法,此方法有返回值
*
* @author hanchao 2018/3/12 8:59
**/
@Override
public Integer call() throws Exception {
Integer interval = RandomUtils.nextInt(1000, 5000);
Thread.sleep(interval);
return interval;
}
/**
* 实现Callable示例
*
* @author hanchao 2018/3/12 9:00
**/
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
//Future、Callable一般与Executor结合使用
//Executor负责创建线程池服务
//实现Callable接口形成的线程类,负责处理业务逻辑,并将处理结果返回
//Future接口负责接收Callable接口返回的值
ExecutorService executor = Executors.newFixedThreadPool(5);
try {
//定义一组返回值
Future<Integer>[] futures = new Future[5];
//向线程池提交任务
for (int i = 0; i < 5; i++) {
//注意Future的参数化类型要与Callable的参数化类型一致
futures[i] = executor.submit(new MyCallableImpl());
}
//输出执行结果
for (int i = 0; i < 5; i++) {
LOGGER.info(futures[i].get());
}
}finally {//将关闭线程池放在finally中,最大限度保证线程安全
//记得关闭这个线程池
executor.shutdown();
}
}
}
运行结果:
2018-03-12 10:44:28 INFO MyCallableImpl:50 - 1564
2018-03-12 10:44:30 INFO MyCallableImpl:50 - 2992
2018-03-12 10:44:30 INFO MyCallableImpl:50 - 1629
2018-03-12 10:44:30 INFO MyCallableImpl:50 - 1454
2018-03-12 10:44:30 INFO MyCallableImpl:50 - 1941
前面只展示了Future类的get()方法。
为了更详细的体现**[实现Callable接口]**这种方式的优点,对Future接口的其他方法进行简单学习。
Future接口的主要方法如下:
说明:
下面通过两段代码进行实例学习。
代码段1:学习了**isDone、isCancelled、get()**方法
System.out.println();
//Future接口方法简单展示: isDone/isCancelled/get()
//创建单线程池
ExecutorService executor1 = Executors.newSingleThreadExecutor();
//向线程池提交任务
Future<Integer> future = executor1.submit(new MyCallableImpl());
try {
//计算执行时间
Long begin = System.currentTimeMillis();
LOGGER.info("future开始执行任务...当前时间:" + begin);
LOGGER.info("通过future.isDone()判断任务是否计算完成:" + future.isDone());
LOGGER.info("通过future.isCancelled()判断任务是否取消:" + future.isCancelled());
LOGGER.info("通过future.get()获取任务的计算结果(从任务开始就一直等待,直至有返回值):" + future.get());
LOGGER.info("future结束执行任务...共计用时:" + (System.currentTimeMillis() - begin) + "ms..\n");
}finally {//将关闭线程池放在finally中,最大限度保证线程安全
LOGGER.info("通过future.isDone()判断任务是否计算完成:" + future.isDone());
LOGGER.info("通过future.isCancelled()判断任务是否取消:" + future.isCancelled());
//记得关闭这个线程池
executor1.shutdown();
}
代码段1运行结果:
2018-03-12 11:02:28 INFO MyCallableImpl:66 - future开始执行任务...当前时间:1520823748695
2018-03-12 11:02:28 INFO MyCallableImpl:67 - 通过future.isDone()判断任务是否计算完成:false
2018-03-12 11:02:28 INFO MyCallableImpl:68 - 通过future.isCancelled()判断任务是否取消:false
2018-03-12 11:02:31 INFO MyCallableImpl:69 - 通过future.get()获取任务的计算结果(从任务开始就一直等待,直至有返回值):2830
2018-03-12 11:02:31 INFO MyCallableImpl:70 - future结束执行任务...共计用时:2843ms..
2018-03-12 11:02:31 INFO MyCallableImpl:72 - 通过future.isDone()判断任务是否计算完成:true
2018-03-12 11:02:31 INFO MyCallableImpl:73 - 通过future.isCancelled()判断任务是否取消:false
代码段2:学习了**isDone、isCancelled、cancel()、get(long,TimeUnit)**方法
//get(long,TimeUnit):最多等待多长时间就不再等待
//创建单线程池
ExecutorService executor2 = Executors.newSingleThreadExecutor();
//向线程池提交任务
Future<Integer> future2 = executor2.submit(new MyCallableImpl());
Long begin2 = System.currentTimeMillis();
try {
LOGGER.info("future开始执行任务...当前时间:" + begin2);
LOGGER.info("通过future.get(long,TimeUnit)获取任务的计算结果(5秒钟之后再获取结果):" + future2.get(500,TimeUnit.MILLISECONDS));
LOGGER.info("future结束执行任务...共计用时:" + (System.currentTimeMillis() - begin2) + "ms..\n");
}catch (TimeoutException e){
LOGGER.info("在限定时间内没有等到查询结果,不再等待..");
//关闭任务
LOGGER.info("当前任务状态:future2.isDone() = " + future2.isDone());
LOGGER.info("当前任务状态:future2.isCancelled() = " + future2.isCancelled());
LOGGER.info("通过future.cancel()取消这个任务:");
future2.cancel(true);
LOGGER.info("当前任务状态:future2.isDone() = " + future2.isDone());
LOGGER.info("当前任务状态:future2.isCancelled() = " + future2.isCancelled());
//关闭线程池
executor2.shutdown();
//意料之中的结果,无需打印日志
//e.printStackTrace();
}
代码段2运行结果:
2018-03-12 11:03:15 INFO MyCallableImpl:85 - future开始执行任务...当前时间:1520823795611
2018-03-12 11:03:16 INFO MyCallableImpl:89 - 在限定时间内没有等到查询结果,不再等待..
2018-03-12 11:03:16 INFO MyCallableImpl:91 - 当前任务状态:future2.isDone() = false
2018-03-12 11:03:16 INFO MyCallableImpl:92 - 当前任务状态:future2.isCancelled() = false
2018-03-12 11:03:16 INFO MyCallableImpl:93 - 通过future.cancel()取消这个任务:
2018-03-12 11:03:16 INFO MyCallableImpl:95 - 当前任务状态:future2.isDone() = true
2018-03-12 11:03:16 INFO MyCallableImpl:96 - 当前任务状态:future2.isCancelled() = true