创建线程的方式一共有4种,这里只说明前3种。
1、继承Thread类
重写run()方法,调用start方法启动
public class Demo1 {
public static void main(String[] args) {
MyThread th1 = new MyThread();
th1.start(); // 注:一定要是使用start()方法调用
System.out.println("我是主进程");
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println("我是子线程");
}
}
输出结果:
我是主进程
我是子线程
注意:一定要使用start()方法,启动,否则仅仅调用MyThread类的run()方法了。
2、实现Runnble接口
实现Runnble接口,再将实现类作为参数传入Thread
public class Demo2 {
public static void main(String[] args) {
MyRunnble runnble = new MyRunnble();
Thread th1 = new Thread(runnble);
th1.start();
System.out.println("我是主进程");
}
}
class MyRunnble implements Runnable{
@Override
public void run() {
System.out.println("我是子进程");
}
}
结果:
我是主进程
我是子进程
其实前面两种创建线程的方式一样的,都是实现Runnble接口在此看下Thread的源码:
我们会发现,Thread类也继承了Runnble接口,我们选择继承Thread的形式,重写run()方法,实质上也是实现Runnble接口。
3、使用Callable、FutureTask
实现Callable接口,并将实现类作为参数传入FutureTask,再将FutureTask 作为参数传入Thread
public class Demo3 {
public static void main(String[] args) throws Exception{
MyCallable myCallable = new MyCallable();
FutureTask<String> f = new FutureTask<>(myCallable);
Thread th = new Thread(f);
th.start();
String s = f.get();
System.out.println(s);
System.out.println("hahha");
}
}
class MyCallable implements Callable{
@Override
public Object call() throws Exception {
Thread.sleep(10000);
return "hello world";
}
}
注意:
1、Callable#call()方法有返回值,返回值的类型需要定义为FutureTask的泛型(如果不了解泛型,可以把泛型看作“特殊的Object对象”)
2、FutureTask#get()可以获取call()方法的返回值,get()方法是一个阻塞方法。这个在后续章节细讲
4、线程池
这个会在之后单独用一个系列来分析
几种方式的区别
1、Thread和Runnble的区别
这个我想大家应该都清楚,主要是程序扩展性问题。推荐使用实现接口的方式。如果一个类继承了Thread类就不能在继承其他的类了(java单继承,多实现),但是接口没有这种限制
2、Runnble和Callable的区别
(1)、run()方法没有返回值,call()方法有返回值
(2)、run()方法不能抛出异常(run方法一旦遇到异常,会导致本线程死掉),call()方法可以抛出异常(如果不调用FutureTask#get()方法,即使有异常也不会抛出)
eg:没有调用get方法,没有抛出异常
public class Demo3 {
public static void main(String[] args) throws Exception{
MyCallable myCallable = new MyCallable();
FutureTask<String> f = new FutureTask<>(myCallable);
Thread th = new Thread(f);
th.start();
// String s = f.get();
// System.out.println(s);
System.out.println("hahha");
}
}
class MyCallable implements Callable{
@Override
public Object call() throws Exception {
System.out.println(5/0);
return "hello world";
}
}
hahha
调用get方法,抛出异常
public class Demo3 {
public static void main(String[] args) throws Exception{
MyCallable myCallable = new MyCallable();
FutureTask<String> f = new FutureTask<>(myCallable);
Thread th = new Thread(f);
th.start();
String s = f.get();
System.out.println(s);
System.out.println("hahha");
}
}
class MyCallable implements Callable{
@Override
public Object call() throws Exception {
System.out.println(5/0);
return "hello world";
}
}
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at cn.gdh.thread.create.Demo3.main(Demo3.java:14)
Caused by: java.lang.ArithmeticException: / by zero
at cn.gdh.thread.create.MyCallable.call(Demo3.java:26)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.lang.Thread.run(Thread.java:748)