创建线程有三种方式
1.继承Thread
2.实现Runnable接口
3.实现Callable接口
一.继承Thread,重写run方法,用start启动,(调用start()方法与run()方法的区别:调用run不会起新的线程,调用start会启用新的线程)
/**
* 线程任务类
* 创建线程的三种方法一
* 继承Thread类,重写run方法,用start启动
*/
public class testThreadOne extends Thread{
public void run(){
for (int i = 0; i < 20; i++) {
System.out.println("线程名称:"+this.getName()+";任务"+i);
}
}
public static void main(String[] args) {
//第一种方法:继承Thread类重写run方法,用start启动线程
//1.调用run方法,不会起新的线程;
//2.调用start方法,会起新的线程;
testThreadOne testThread = new testThreadOne();
testThreadOne testThread1 = new testThreadOne();
testThread.start();
testThread1.run();
}
}
二.实现Runnable接口,重写run方法,用start方法启动
/**
* 线程任务类
* 创建线程三种方法二
* 实现Runnable接口来创建线程
* 1.实现Runnable接口,并重写run方法
* 2.创建一个任务类的对象
* 3.任务类必须在线程中执行,因此将任务类的对象作为参数,创建一个Thread类对象,该Thread类对象才是真正的线程对象
* 4.用start起订线程
* 注:
* 变量隔离
* 锁
*
*/
public class testThreadTwo implements Runnable{
public static void main(String[] args) {
testThreadTwo testThreadTwo = new testThreadTwo();
testThreadTwo testThreadTwo1 = new testThreadTwo();
Thread t1 = new Thread(testThreadTwo);
Thread t2 = new Thread(testThreadTwo1);
t1.start();
t2.start();
}
@Override
public void run() {
for (int i = 0; i <20; i++) {
System.out.println("线程名称:"+Thread.currentThread().getName()+";任务"+i);
}
}
}
三.实现Callbale接口重写call方法,且利用FutureTask
/**
* 线程任务类
* 创建线程三种方法三
* 1.创建一个任务类,实现Callable接口,并实现call()方法,call()方法中的内容就是需要线程完成任务,且有返回值
* 2,创建一个任务类的对象,并使用FutureTask类来包装任务类的对象,该FutureTask对象封装了任务类对象中call()方法的返回值
* 3.任务类必须在线程中执行,因此将FutureTask类的对象作为参数,创建一个Thread类对象,该Thread类对象才是整整的线程对象
* 4,调用Thread线程类对象的start()方法,来起订一个线程。
* 5,调用FutureTask类对象的get()方法来获取线程执行的返回值,即人物类对象call()方法的返回值
*/
public class testThreadThree implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int i = 0;
for (i = 0; i < 20; i++) {
if (i == 5)
break;
System.out.println(Thread.currentThread().getName() + "--- " + i);
}
return i;
}
public static void main(String[] args) {
testThreadThree tt = new testThreadThree();
FutureTask<Integer> ft = new FutureTask<>(tt);
Thread thread = new Thread(ft);
thread.start();
try {
System.out.println(Thread.currentThread().getName() + " main方法里面的参数 " + ft.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}
三种方法比较:
1.继承Thread类的方法:
优点:编写容易,任务类中访问当前线程可以使用this关键字
缺点:任务类即线程类已经继承了Thread类,所以不能再继承其他父类
2.实现Runnable接口的方式:
优点:任务类只实现了Runnable接口,还可以继承其他类,可以多个线程对象共享一个任务类对象,即多线程共享一份资源的情况下
缺点:编写稍微复杂,任务类中访问当前线程,必须使用Thread.currentThread()方法
3.实现Callable和Future的方式
优点:任务类只是实现了Callable接口,还可以继承其他类,同样多线程下可以共享一份资源,这种方式还有返回值,并且可以抛出返回值的异常
缺点:编写复杂,任务类中访问当前线程时,必须使用Thread.currentThread()方法
总结:
仅仅只重写run()方法,而不是重写Thread类其他方法的前提下,比较推荐实现Runnable接口的方式创建线程
因为不仅修改或者增强类的能力,不应该为其创建子类,而且实现Runnable接口的方式,线程和资源相对分离,程序更加健壮,更符合面向对象的变成思想,当然,需要线程有返回值时可以使用Callable的方式,但Callable的方式有一个问题,当调用get()方法时,如果线程还未执行完毕则会阻塞到线程执行完毕拿到返回值