一般来说创建线程的方法有三种,分别是:
第一步:定义一个类继承Thread类,并实现run()方法,run()方法中的内容即为线程需要完成的功能
class MyThread extends Thread{ String name; public MyThread(String name) { this.name = name; System.out.println("继承Thread类"); } @Override public void run() { for(int i = 1; i < 50; i++) { System.out.println(this.name + ":" + i); } } }第二步:构建子类对象,并调用对象的start()方法启动线程
public class createThread { public static void main(String[] args) { //创建子类的对象 MyThread thread1 = new MyThread("one"); MyThread thread2 = new MyThread("two"); //启动线程 thread1.start(); thread2.start(); } }第一步:定义一个类实现Runnable接口,并实现接口中的run()方法
class MyThread2 implements Runnable{ @Override public void run() { // System.out.println("this"+this); // System.out.println("Thread" + Thread.currentThread()); for(int i = 0; i < 50; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } }第二步:创建该类对象,再创建一个Thread类对象,并把实现Runnable接口的类的对象作为参数传给Thread类对象,并调用start()方法
public class createThread { public static void main(String[] args) { //创建Runnable类对象 MyThread2 t1 = new MyThread2(); MyThread2 t2 = new MyThread2(); //把对象作为实参传递给Thread类 Thread thread3 = new Thread(t1,"three"); Thread thread4 = new Thread(t2,"four"); thread3.start(); thread4.start(); }注意:创建的Runnable实现类只是一个target,不是线程对象,它用来指明线程运行时需要做的任务,Thread类的对象才是线程对象。
实现Runnable接口来创建一个类的好处:
一个Runnable实现类可以传给多个线程对象,所以适合多个相同程序代码的线程去处理同一个资源的情况,把线程和代码、数据有效的分离。Thread类创建线程是采用继承的方式,而Java中只能单继承。如果某个类的子类需要创建线程,那么就只能采用实现Runnable接口或者实现Callable接口的方式。先定义一个Callable的实现类,并重写call()方法。call()有返回值。
class MyThread3 implements Callable<Integer>{ @Override public Integer call() throws Exception { int a = new Random().nextInt(100); System.out.println("线程执行结果 "+a); return a; } }Callable类型的任务可以有两种执行方式:
第一种是借助FutureTask执行
创建Callable实现类的对象,并作为参数传给FutureTask对象,FutureTask作为参数传给Thread类对象,并执行start()方法
Callable<Integer> t4 = new MyThread3(); FutureTask<Integer> futuretask = new FutureTask<Integer>(t4); new Thread(futuretask).start();futuretask.get()可以获取Callable实现类的返回值
第二种是借助线程池来执行
先创建一个线程池,然后调用线程池的submit方法,并将Callable实现类作为参数传入。线程池的submit()方法返回值为Future对象,所以用一个Future对象来接收。
ExecutorService exe = Executors.newCachedThreadPool(); Future<Integer> future = exe.submit(new MyThread3());future.get()可以获取Callable实现类的返回值
特别的是,当执行多个Callable任务,有多个返回值时,我们可以定义一个Future类的集合来接收
//Callable实现类 class ManyCallableTasks implements Callable<String>{ private int id; //重写构造方法,为每个线程加上id public ManyCallableTasks(int id) { this.id = id; } //重写call()方法,返回值为字符串 @Override public String call() throws Exception { for(int i = 0; i < 5; i++) { System.out.println("Thread"+id); Thread.sleep(1000); } return "Result of callable" + id; } } public class ManyCallableThreads { public static void main(String[] args) { //创建线程池 ExecutorService exe = Executors.newCachedThreadPool(); //创建Future的集合,用来存放所有线程 ArrayList<Future<String>> results = new ArrayList<Future<String>>(); //线程作为线程池的参数,传入后线程池执行submit()方法,返回值装入results for(int i = 0; i < 5; i++) { results.add(exe.submit(new ManyCallableTasks(i))); } for(Future<String> fs : results) { if(!fs.isDone()) { try { System.out.println(fs.get()); }catch (Exception e) { e.printStackTrace(); } }else { System.out.println("任务未完成"); } } exe.shutdown(); } }代码来自:https://blog.csdn.net/sunp823/article/details/51569314
Callable的好处:
有返回值call()可以抛出异常运行Callable任务可以得到一个Future对象,表示异步计算的结果。它提供了检测计算是否完成的方法(isDone())以等待计算的完成,并检索计算的结果。这三种方式在底层实现上有什么区别呢,我们来看看它们的源码。(本文在引用源码过程中删除了部分注释,但是删除的部分所说的内容我都做了中文的说明,如果有遗漏的地方还请各位指出,想看注释的可以自行查看源码)
我们可以发现Runnable接口中只有一个run()方法,注释中说:当一个类实现Runnable接口来创建一个线程时,开启线程会导致对象的run方法在单独执行的线程中被调用,run方法主要功能是指明线程做什么工作,参考Thread类中的run 方法。那么我们来看Thread类:
public class Thread implements Runnable我们可以发现Thread类其实也实现了Runnable接口,我们看看它重写的run()方法:
/** * If this thread was constructed using a separate * <code>Runnable</code> run object, then that * <code>Runnable</code> object's <code>run</code> method is called; * otherwise, this method does nothing and returns. * <p> * Subclasses of <code>Thread</code> should override this method. * * @see #start() * @see #stop() * @see #Thread(ThreadGroup, Runnable, String) */ @Override public void run() { if (target != null) { target.run(); } }这个target就是Runnable实现类的对象,所以这个方法的意思就是如果线程是通过实现Runnable接口来创建的话,那么就会调用Runnable对象的run()方法。其实这个也印证了为什么我们在使用Runnable接口创建线程时需要先实例化Runnable接口的实现类,然后再把这个对象作为参数传入Thread类中,其实是把任务的运行方式传入线程中
接下来我们看一下Thread类:
Thread类有很多构造方法,但是所有的构造方法本质上都是调用的同一个init方法,只是传入的参数不同。
/** * Initializes a Thread. * * @param g the Thread group group:线程组 * @param target the object whose run() method gets called target:表示谁的run方法被调用 * @param name the name of the new Thread name:新建线程的名字 * @param stackSize the desired stack size for the new thread, or * zero to indicate that this parameter is to be ignored. stackSize:指定的新建线程的大小,如果是0表示这个参数被忽略了 * @param acc the AccessControlContext to inherit, or * AccessController.getContext() if null acc:继承的AccessControlContext,如果为空表示这个值是get方法获取的值 */ private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc)这个接口和Runnable接口很像,里面只有一个call方法,返回类型是泛型,返回的是计算结果,如果不能计算结果那就抛出异常。适合会返回结果而且可能抛出异常的一种任务,它的实现类只需要定义一个没有参数的方法call(),Callable接口很像Runnable接口,他们都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。Executors 类包含一些从其他普通形式转换成 Callable 类的实用方法。
我们在使用Callable接口创建线程时会用到Future接口
源码注释中说:Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如有必要,计算完成前可以阻塞此方法。取消则由 cancel 方法来执行。还提供了其他方法,以确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明 Future<?> 形式类型、并返回 null 作为底层任务的结果。注释中还提供了示例,这里不再展示。
说一下Future中的几个方法:
boolean cancel(boolean mayInterruptIfRunning);取消对此任务的执行,但是如果遇见如下情况:1、该任务已被取消;2、该任务已经完成;3、因为某些原因无法取消,那么取消会失败。调用此方法时如果该任务还未启动,那么这个任务将永不运行;如果该任务已经启动,那么该任务是停止还是继续执行将取决于mayInterruptIfRunning,如果值为true,该任务的线程需中断,否则允许该任务执行完成。
boolean isCancelled();判断该任务在正常完成前是否被取消。
boolean isDone();判断该任务是否完成,任务正常完成、正常终止、异常或取消都是完成,将返回true。
V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;这两个方法都是等待计算完成并获取它的结果,但是第二个方法传入了一个时间,表示最多等待timeout时间就获取它的结果( 如果结果可用)。(TimeUnit是timeout的单位,为枚举类,有固定取值)
在使用Future创建线程时,使用了Executors中的submit方法,
我们再来看看FutureTask类
FutureTask类表示可取消的异步计算。其实在Future接口中,有一段注释讲了FutureTask类:FutureTask 类是 Future 的一个实现, Future 可实现 Runnable,所以可通过 Executor 来执行。
此类中有开始计算、取消计算、查询计算是否完成和获取计算结果的方法,此类提供了对 Future 的基本实现。仅在计算完成时才能获取结果;如果计算尚未完成,则阻塞 get 方法。一旦计算完成,就不能再重新开始或取消计算(除非调用runAndReset 方法)。
public class FutureTask<V> implements RunnableFuture<V>FutureTask实现的是RunnableFuture接口,
public interface RunnableFuture<V> extends Runnable, Future<V> { /** * Sets this Future to the result of its computation * unless it has been cancelled. */ void run(); }RunnableFuture接口其实继承了Runnable接口和Future接口,有这两个接口的特性,而且有run()方法。所以可以把FutureTask类的对象作为参数传给Thread类,它作为参数时和Runnable实现类对象等效。