方法1:继承Thread类
1定义一个子类继承线程类java.long.Thread ,重写run()方法
2创建一个子类对象
3 调用线程对象的start方法启动线程(启动后还是执行run方法)
第一种方法
public class MyThread extends Thread {
@Override
//run方法里面写要执行的东西
public void run() {
// TODO Auto-generated method stub
super.run();
for(int i=0;i<5;i++) {
System.out.println("子线程"+i);
}
}
}
第一种方法
public class Test1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Thread t=new MyThread();//创建线程对象
t.start();//调用线程的启动方法
for(int i=0;i<5;i++) {
System.out.println("主线程"+i);
}
}
}
方式1的优缺点?
优点编码简单
缺点:存在单继承的局限性,线程类已经继承了Thread,无法继承其他类,不利于扩展
为什么不直接调用run方法,而是调用strat启动线程?
直接调用run方法会当成普通方法执行,此时相当于还是单线程
只有调用start方法才是启动一个新的线程执行
为什么不能把主线程任务放在子线程之前?
这样主线程一直就是先跑完的,相当于一个单线程的效果了
多线程的创建方式二(实现Runnable接口)
1定义一个线程任务类MyRunnable实现Runnable接口,重新run方法
2创建一个MyRunnable任务对象
3把任务对象交给 Thread线程对象处理
4调用线程对象的start()方法启动线程
Thread构造器
public Thread(String name) 可以为当前线程指定名称
public Thread (Runnable target) 封装Runnable对象成线程对象
Public Thread (Runnable target, String name )封装Runnable 对象成线程对象并指定线程名称
//第二种方法
public class MyRunnable implements Runnable{
@Override
//写线程任务
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<5;i++) {
System.out.println("子线程被执行"+i);
}
}
}
package 多线程;
public class Test2 {
//第二种方法
public static void main(String[] args) {
// TODO Auto-generated method stub
Runnable r=new MyRunnable(); //创建任务对象
Thread t=new Thread(r);//封装成线程对象
t.start(); //启动线程
for(int i=0;i<5;i++) {
System.out.println( "主线程被执行"+i);
}
}
}
方式二优缺点
优点:线程任务类只实现了接口,可以继续继承类和实现接口扩展性强
缺点:编程多一层对象包装,如果线程有执行结果不可以直接返回的
线程创建方式2(匿名内部类写法)
//第二种方式的匿名内部类写法
public class Test5 {
public static void main(String[] args) {
Thread t=new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<5;i++) {
System.out.println("子线程"+i);
}
}
});
t.start();
for(int i=0;i<5;i++) {
System.out.println("主线程被执行"+i);
}
}
}
可以用Lamubda表达式继续简化为
Thread t=new Thread(()-> {
for(int i=0;i<5;i++) {
System.out.println("子线程"+i);
}
});
前两种创建方式都存在一个问题?
他们重写的run方法均不能直接返回结果
不适合需要返回线程执行结果的业务场景
怎么解决这个问题呢?
JDK5.0提供了Callable和FutureTask来实现
第三种方式:利用Callable、FutureTask接口实现
1得到任务对象 定义类实现Callable接口,重写call方法,封装要做的事情;用FutureTask把Callable对象封装成线程对象
2把线程对象交给Thread处理
3调用Thread的start方法启动线程,执行任务
4线程执行完毕,通过FutureTask的get方法获取任务执行的结果
//第三种方式
import java.util.concurrent.Callable;
public class Threesum implements Callable<Integer>{
private int a;
private int b;
public Threesum(int a, int b) {
super();
this.a = a;
this.b = b;
}
//写要执行的任务
//我的功能是求和
@Override
public Integer call() throws Exception {
// TODO Auto-generated method stub
int sum=a+b;
return sum;
}
}
主函数
//第三种方式
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Test6 {
public static void main(String[] args) {
// 创建Callable对象
Callable<Integer> ca=new Threesum(10,20);
//把Callable 对象包装为 FutureTask对象
FutureTask<Integer> fu=new FutureTask<>(ca);
//FutureTask交给线程对象Thread
Thread s=new Thread(fu);
//开始执行
s.start();
//看结果用FutureTask的get方法
try {
int sum=fu.get();
System.out.println("第三种方法写的线程输出结果为:"+sum);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
方式三优缺点
优点:线程任务类只是实现接口,可以继续继承和实现接口,扩展性强
可以在线程执行完毕后去获取线程执行的结果
缺点:代码复杂一点