创建线程方式
方式一、创建一个任务类实现Runnable接口,并将其具体对象提交给Thread构造器
创建一个发射类LiftOff实现Runnable接口:
package concurrency; public class LiftOff implements Runnable { protected int countDown = 10; // Default private static int taskCount = 0; private final int id = taskCount++; public LiftOff() { } public LiftOff(int countDown) { this.countDown = countDown; } public String status() { return Thread.currentThread() + "#" + id + "(" + (countDown > 0 ? countDown : "Liftoff!") + "), "; } public void run() { while (countDown-- > 0) { System.out.println(status()); Thread.yield(); } } }
以上代码中调用了Thread.yield()方法,该方法的作用是建议线程调度器切换到其它线程执行任务,注意,只是建议,不保证采纳;
创建完任务类之后,可以在Main函数中使用LiftOff对象创建一个Thread对象,并调用其start方法启动该线程,如下:
package concurrency; public class BasicThreads { public static void main(String[] args) { Thread t = new Thread(new LiftOff()); t.start(); System.out.println(Thread.currentThread() + "Waiting for LiftOff"); } }
打印结果如下,注意该程序中是同时存在两个线程(main和Thread-0)在运行的;
另外关于Thread对象的打印形式为[Thread-0,5,main],其中依次代表[线程名,线程优先级、线程组名], 具体可查看Thread类的toString方法;
Thread[main,5,main]Waiting for LiftOff Thread[Thread-0,5,main]#0(9), Thread[Thread-0,5,main]#0(8), Thread[Thread-0,5,main]#0(7), Thread[Thread-0,5,main]#0(6), Thread[Thread-0,5,main]#0(5), Thread[Thread-0,5,main]#0(4), Thread[Thread-0,5,main]#0(3), Thread[Thread-0,5,main]#0(2), Thread[Thread-0,5,main]#0(1), Thread[Thread-0,5,main]#0(Liftoff!),
方式二、继承Thread类,调用其具体对象的start方法
package concurrency; public class SimpleThread extends Thread { private int countDown = 5; private static int threadCount = 0; public SimpleThread() { // Store the thread name: super(Integer.toString(++threadCount)); start(); } public String toString() { return "#" + getName() + "(" + countDown + "), "; } public void run() { while (true) { System.out.println(this); if (--countDown == 0) return; } } public static void main(String[] args) { for (int i = 0; i < 5; i++) new SimpleThread(); } }
对比通过实现Runnable接口的方式,该方式不建议使用,因为java的单继承机制,通常通过实现接口比继承会更好点;
另外还可以通过内部内部类将线程代码隐藏在类中,如下写法;
class InnerThread1 { private int countDown = 5; private Inner inner; private class Inner extends Thread { Inner(String name) { super(name); start(); } public void run() { try { while (true) { print(this); if (--countDown == 0) return; sleep(10); } } catch (InterruptedException e) { print("interrupted"); } } public String toString() { return getName() + ": " + countDown; } } public InnerThread1(String name) { inner = new Inner(name); } }
方式三、创建一个任务类实现Runnable接口,并将其具体对象提交给Executors【推荐】
java.util.concurrent包中的执行器Executors可以帮助我们管理Thread对象,简化并发编程,如下,可以使用Executors类中的newCachedThreadPool静态方法创建一个可缓存的线程池,并用其执行相关任务;
package concurrency; import java.util.concurrent.*; public class CachedThreadPool { public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); for (int i = 0; i < 5; i++) exec.execute(new LiftOff()); exec.shutdown(); } }
在Executors类中,除了通过newCachedThreadPool创建线程池外,还可以创建通过以下方法创建其它种类的线程池:
newFixedThreadPool:固定大小度的线程池
newSingleThreadExecutor:单线程线程池
newScheduledThreadPool:执行定时和周期性任务
方式四、创建一个任务类实现Callable接口,并将其具体对象提交给Executors【推荐】
实现Callable接口的类同样是一个任务类,与实现Runnable接口的区别是该方式可以有返回值;
在实现Callable接口的类中,线程执行的方法是call方法(有返回值),而不是run方法;
在main方法中可以通过调用ExecutorService的submit方法,返回一个Future对象,通过该对象可以获取线程运行的返回值,注意需要等Future完成后才能取得结果,可以通过isDone方法来查询Future是否已完成,或者直接调用get方法来获取(会阻塞,直到结果准备就绪)。
package concurrency; import java.util.concurrent.*; import java.util.*; class TaskWithResult implements Callable<String> { private int id; public TaskWithResult(int id) { this.id = id; } public String call() { return "result of TaskWithResult " + id; } } public class CallableDemo { public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); ArrayList<Future<String>> results = new ArrayList<Future<String>>(); for (int i = 0; i < 10; i++) results.add(exec.submit(new TaskWithResult(i))); for (Future<String> fs : results) try { System.out.println(fs.get()); } catch (InterruptedException e) { System.out.println(e); return; } catch (ExecutionException e) { System.out.println(e); } finally { exec.shutdown(); } } }
小结
其实,更普遍的,我觉得创建线程就两种形式:
- 直接通过new Thread创建线程(可传入任务对象);
- 创建任务对象提交给Executors去创建(其实内部的线程工厂也是通过new Thread创建);
另外,这里的任务对象也有两种方式创建,通过实现Runnable接口和实现Callable接口;