1、用Thread创建线程:继承Thread类,重写Thread类的run()方法,用Thread子类的实例调用start()方法就开启了一个运行run()函数的线程。
1.1可以直接创建一个子类,用子类实例调用start()方法:
public class MyClassThread extends Thread {
public void run(){
System.out.println(Thread.currentThread().getName());
}
}
启动线程时可以:
new MyClassThread().start();
1.2可以创建一个匿名子类:
new Thread(){
public void run(){
System.out.println("匿名子类");
}
}.start();
2、用Runnable创建线程:实现TRunnable接口,重写Runnable接口的run()方法,用实现类的实例调用start()方法就开启了一个运行run()函数的线程。
2.1可以创建一个实现Runnable接口的类,让其实例调用start()方法:
public class MyClassRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
启动线程时要把实现Runnable类的实例传到Thread的构造器里:
new Thread(new MyClassRunnable()).start();
2.2也可以创建匿名子类:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Runnable匿名子类");
}
}).start();
2.3通过创建线程池submit()方法执行Runnable(这种方式可以获得一个返回值):
先看一下submit()源码:
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
示例:
ExecutorService executorService = Executors.newCachedThreadPool();
//用线程池执行Runnable并获取一个返回结果
Future<String> futureRun = executorService.submit(new Runnable() {
@Override
public void run() {
String s = "执行了";
System.out.println("通过线程池执行Runnable");
}
},s);
也可以通过这种方式:
Future futureRun2 = executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("没有返回结果");
}
});
2.4用工厂类Executors静态方法将Runnable封装成Callable,然后用Callable的启动方法去启动。源码:
public static Callable<Object> callable(Runnable task)
public static <T> Callable<T> callable(Runnable task, T result)
示例:
//将Runnable封装成Callable Callable callable = Executors.callable(new Runnable() { @Override public void run() { System.out.println("封装成Callable的Runnable"); } }); final String[] str = new String[1]; Callable<String> callable1 = Executors.callable(new Runnable() { @Override public void run() { str[0] = "执行了"; System.out.println("封装成Callable的Runnable,并且有返回结果"); } },str[0]);
3、Thread和Runnable的比较:
Thread实现了Runnable;
在面向对象思想中,Runnable实例是一个作业,Thread实例才是要执行作业的线程。将作业定义好,传到Thread里,是组合关系,相比于继承Thread,松耦合;
用Thread需要继承,Java中不能多继承,而可以实现多个接口。这是另一个Runnable的好处。
4、用Callable创建线程:
4.1 Callable和Runnable的区别:
4.1.1前者通过call()方法设置线程执行任务,后者通过run()方法。
4.1.2Callable的源码:
public interface Callable<V> {
V call() throws Exception;
}
可见Callable是一个泛型参数化的接口。call()方法是有返回值而且能抛出异常的,这是比run()方法多的特性。
4.2通过线程池submit()方法执行Callable:Callable不像Runnable接口能把实例放到Thread构造器中,由Thread的start()方法启动。Callable通常当做线程池submit()方法的参数传入以启动,并且submit()方法会返回Future实例,用于对异步计算结果的获取。Future<V>源码:
public interface Future<V> { boolean cancel(boolean mayInterruptIfRunning); boolean isCancelled(); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
(1)当FutureTask处于未启动或已启动状态时,如果此时我们执行FutureTask.get()方法将导致调用线程阻塞;当FutureTask处于已完成状态时,执行FutureTask.get()方法将导致调用线程立即返回结果或者抛出异常。
(2)当FutureTask处于未启动状态时,执行FutureTask.cancel()方法将导致此任务永远不会执行。
当FutureTask处于已启动状态时,执行cancel(true)方法将以中断执行此任务线程的方式来试图停止任务,如果任务取消成功,cancel(...)返回true;但如果执行cancel(false)方法将不会对正在执行的任务线程产生影响(让线程正常执行到完成),此时cancel(...)返回false。
当任务已经完成,执行cancel(...)方法将返回false。
运行Callable示例:
//用Callable(通过线程池)执行线程
ExecutorService executorService = Executors.newCachedThreadPool();
Future future = executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
String s = "Callable";
System.out.println(s);
return s;
}
});
4.3将Callable作为参数传入FutureTask构造器中,通过FutureTask执行:事实上FutureTask实现了RunnableFuture<V>接口,而RunnableFuture<V>接口继承了Runnable接口和Future<V>接口,所以FutureTask可以放到Thread的构造器里去运行:
//用Callable(通过FutureTask封装)执行线程 FutureTask futureTask = new FutureTask(new MyClassCallable()); Thread thread = new Thread(futureTask); thread.start();