常用的创建线程的方法有三种:
继承Thread类
实现Runnable接口
实现Callable接口
我们来说说继承Thread类 :
package lesson01;
public class Thread_Class {
public static void main(String[] args) {
//创建子线程
MyThread myThread = new MyThread();//创建一个线程的对象
myThread.start();//开启子线程
for (int i = 0; i < 100; i++) {
System.out.println("执行了主线程");
}
}
}
class MyThread extends Thread{
@Override
public void run(){//子线程
for (int i = 0; i < 100; i++) {
System.out.println("执行了子线程");
}
}
}
创建线程的步骤:
写一个类名叫MyThread的类继承Thread类,Thread是一个线程包下的已经被别人写好的类,MyThread则是我们创建的子线程要执行的类
在MyThread里重写run()方法,@Override就是表示重写的意思
然后我们在主程序里创建一个MyThread类的对象,我们要通过这个对象来开启线程
我们通过start()的方法启动子线程
容易产生的疑惑:
为什么要重写run()方法?
因为继承Thread父类里的run()方法没有我们要的功能
为什么不直接调用run()方法,而去调用start()方法?
因为myThread.run()表示调用函数,没有开启线程
以上就是通过Thread类创建并启动线程的方法
实现Runnable接口:
package lesson01;
public class Runnable_Interface {
public static void main(String[] args) {
MyRunner myRunner = new MyRunner();
new Thread(myRunner).start();
for (int i = 0; i <1000 ; i++) {
System.out.println("我是main主线程");
}
}
}
class MyRunner implements Runnable{
@Override
public void run() {
for (int i = 0; i <1000 ; i++) {
System.out.println("我是通过Runnable接口创建的子线程");
}
}
}
实现Runnable接口与继承Thread类的区别:
你需要在创建线程对象(myRunner)以后再创建一个Thread类的对象,然后将Runnable实现类(MyRunner)的对象当作参数传入Thread对象,然后通过Thread对象来开启线程,至于为什么要这么搞,它使用到了一个叫“静态代理”的思想,我们会在下一篇文章讨论
new Thread(myRunner).start();//创建对象,调用方法一气呵成
Thread thread = new Thread(myRunner);//一步步来,上面比较简洁
thread.start();
实现Callable接口:
package lesson01;
import java.util.concurrent.*;
public class Callable_Interface {
public static void main(String[] args) throws ExecutionException,InterruptedException {
MyOneCallable mc = new MyOneCallable();
MyTwoCallable md = new MyTwoCallable();
MyThreeCallable me = new MyThreeCallable();
ExecutorService sc = Executors.newFixedThreadPool(3);//开启拥有三条线程的线程池
Future<Boolean> sut = sc.submit(mc);//上交子线程并等待CPU调用 == 开启线程
Future<Boolean> sut2 = sc.submit(md);//上交子线程并等待CPU调用 == 开启线程
Future<Boolean> sut3 = sc.submit(me);//上交子线程并等待CPU调用 == 开启线程
boolean result = sut.get();//获取返回值
boolean result2 = sut2.get();//获取返回值
boolean result3 = sut3.get();//获取返回值
sc.shutdown();//关闭线程池,开启了就一定要关闭,又开又关,再开不难
}
}
class MyOneCallable implements Callable<Boolean>{
@Override
public Boolean call() throws Exception {
for (int i = 0; i < 1000; i++) {
System.out.println("执行了Callable的第一个子线程");
}
return true;
}
}
class MyTwoCallable implements Callable<Boolean>{
@Override
public Boolean call() throws Exception {
for (int i = 0; i < 1000; i++) {
System.out.println("执行了Callable的第二个子线程");
}
return true;
}
}
class MyThreeCallable implements Callable<Boolean>{
@Override
public Boolean call() throws Exception {
for (int i = 0; i < 1000; i++) {
System.out.println("执行了Callable的第三个子线程");
}
return true;
}
}
实现Callable接口创建线程与上面两种方式不同的是,他要创建一个线程池,通过submit()方法开启线程,完事儿以后还要关闭线程
以上就是创建线程的三种方法,逻辑相同,实际写的代码有所差异,Callable的代码看起来多是因为创建了三个子线程
由于cpu执行速度太快,所以我们才将循环的次数弄成1000,不然看不出来这三个线程是交替执行的